En esta página, se describe la arquitectura de CameraX, que incluye la estructura, cómo trabajar con la API y con ciclos de vida, y cómo combinar los casos de uso.
Estructura de CameraX
Puedes usar CameraX para interactuar con la cámara del dispositivo mediante una abstracción llamada caso de uso. Están disponibles los siguientes casos de uso:
- Vista previa: Acepta una plataforma para mostrar una vista previa, como un elemento
PreviewView
. - Análisis de imágenes: Proporciona búferes a los que se puede acceder desde la CPU para análisis, que sirven, por ejemplo, para aprendizaje automático.
- Captura de imágenes: Captura y guarda una foto.
- Captura de video: Captura video y audio con
VideoCapture
Los casos de uso pueden combinarse y estar activos en simultáneo. Por ejemplo, una app puede permitir que el usuario vea lo mismo que la cámara con un caso de uso de vista previa, tenga otro caso de análisis de imágenes para determinar si las personas que aparecen en la foto están sonriendo, y que incluya otro de captura de imágenes que tome una foto cuando efectivamente estén sonriendo.
Modelo de API
Para trabajar con la biblioteca, tienes que especificar lo siguiente:
- El caso práctico deseado con sus opciones de configuración
- Qué hacer con los datos resultantes al juntar objetos de escucha
- Cuál es el flujo pretendido, como los momentos de habilitación de cámaras y de producción de datos, mediante la vinculación del caso de uso a los ciclos de vida de la arquitectura de Android.
Puedes configurar casos de uso con los métodos set()
y finalizarlos con el método build()
. Cada objeto de caso de uso proporciona un conjunto de API específicas de casos de uso. Por ejemplo, el caso de uso de la captura de imagen proporciona una llamada al método takePicture()
.
Una aplicación especificará un ciclo de vida con el que asociar la cámara mediante cameraProvider.bindToLifecycle()
, en lugar de llamar métodos de inicio y finalización en onResume()
y onPause()
.
El ciclo de vida informa a CameraX cuándo se debe configurar la sesión de captura de la cámara y se asegura de que el estado de la cámara cambie de manera adecuada para que coincida con sus transiciones.
Si quieres conocer los pasos de implementación para cada caso de uso, consulta Cómo implementar una vista previa, Cómo analizar imágenes, Cómo capturar imágenes y Cómo capturar video.
Ejemplo de modelo de API
El caso de uso de la vista previa interactúa con un elemento Surface
para mostrarla. Las aplicaciones crean el caso de uso con opciones de configuración que utilizan el siguiente código:
Kotlin
val preview = Preview.Builder().build() val viewFinder: PreviewView = findViewById(R.id.previewView) // The use case is bound to an Android Lifecycle with the following code val camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) // PreviewView creates a surface provider and is the recommended provider preview.setSurfaceProvider(viewFinder.getSurfaceProvider())
Java
Preview preview = new Preview.Builder().build(); PreviewView viewFinder = findViewById(R.id.view_finder); // The use case is bound to an Android Lifecycle with the following code Camera camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview); // PreviewView creates a surface provider, using a Surface from a different // kind of view will require you to implement your own surface provider. preview.previewSurfaceProvider = viewFinder.getSurfaceProvider();
Para ver más ejemplos de código, consulta la app de muestra de CameraX oficial.
Ciclos de vida de CameraX
CameraX obedece un ciclo de vida para determinar cuándo abrir la cámara, crear una sesión de captura, detenerse y apagarse. Las API de casos de uso proporcionan llamadas y devoluciones de llamada de métodos con el fin de supervisar el progreso.
Según se explica en Cómo combinar casos de uso, puedes vincular combinaciones de casos de uso a un solo ciclo de vida. Cuando tu app necesite admitir casos de uso que no se puedan combinar, podrás realizar una de las siguientes acciones:
- Agrupar casos de uso compatibles en más de un fragmento y, luego, intercambiar fragmentos
- Crear un componente del ciclo de vida personalizado y usarlo para controlar el ciclo de vida de la cámara de forma manual
En caso de desacoplar la vista y los LifecycleOwner de los casos de uso de la cámara (por ejemplo, si usas un ciclo de vida personalizado o un fragmento de retención), debes asegurarte de que todos los casos de uso estén desvinculados de CameraX. Para ello, utiliza ProcessCameraProvider.unbindAll()
o desvincula cada caso de uso de forma individual. De manera alternativa, cuando vinculas casos de uso a un ciclo de vida, puedes permitir que CameraX administre la apertura y el cierre de la sesión de captura y la desvinculación de los casos de uso.
Si todas las funciones de tu cámara corresponden al ciclo de vida de un solo componente relacionado con los ciclos de vida, como un fragmento AppCompatActivity
o AppCompat
, el uso del ciclo de vida de ese componente cuando vincules todos los casos de uso deseados garantizará que las funciones de la cámara estén listas cuando el componente esté activo o que se descarten de manera segura y sin consumir recursos.
LifecycleOwner personalizado
Para casos avanzados, puedes crear un LifecycleOwner
personalizado a fin de permitir que tu app controle explícitamente el ciclo de vida de la sesión de CameraX en lugar de vincularlo a un LifecycleOwner
de Android estándar.
En la siguiente muestra de código, se indica cómo crear un LifecycleOwner simple y personalizado:
Kotlin
class CustomLifecycle : LifecycleOwner { private val lifecycleRegistry: LifecycleRegistry init { lifecycleRegistry = LifecycleRegistry(this); lifecycleRegistry.markState(Lifecycle.State.CREATED) } ... fun doOnResume() { lifecycleRegistry.markState(State.RESUMED) } ... override fun getLifecycle(): Lifecycle { return lifecycleRegistry } }
Java
public class CustomLifecycle implements LifecycleOwner { private LifecycleRegistry lifecycleRegistry; public CustomLifecycle() { lifecycleRegistry = new LifecycleRegistry(this); lifecycleRegistry.markState(Lifecycle.State.CREATED); } ... public void doOnResume() { lifecycleRegistry.markState(State.RESUMED); } ... public Lifecycle getLifecycle() { return lifecycleRegistry; } }
Con este LifecycleOwner
, tu app puede colocar transiciones de estado en los puntos deseados de su código. Si quieres obtener más información para implementar esta función en tu app, consulta Cómo implementar un LifecycleOwner personalizado.
Casos de uso simultáneos
Los casos de uso pueden ejecutarse en simultáneo. Si bien pueden estar vinculados de manera secuencial a un ciclo de vida, te recomendamos que vincules todos los casos de uso con una sola llamada a CameraProcessProvider.bindToLifecycle()
. Obtén más información sobre las prácticas recomendadas para los cambios de configuración en Cómo controlar los cambios en la configuración.
En la siguiente muestra de código, la app especifica los dos casos de uso que se crearán y ejecutarán de manera simultánea. También especifica el ciclo de vida que se usará para ambos casos de uso, a fin de que ambos se inicien y se detengan según el ciclo de vida.
Kotlin
private lateinit var imageCapture: ImageCapture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener(Runnable { // Camera provider is now guaranteed to be available val cameraProvider = cameraProviderFuture.get() // Set up the preview use case to display camera preview. val preview = Preview.Builder().build() // Set up the capture use case to allow users to take photos. imageCapture = ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build() // Choose the camera by requiring a lens facing val cameraSelector = CameraSelector.Builder() .requireLensFacing(CameraSelector.LENS_FACING_FRONT) .build() // Attach use cases to the camera with the same lifecycle owner val camera = cameraProvider.bindToLifecycle( this as LifecycleOwner, cameraSelector, preview, imageCapture) // Connect the preview use case to the previewView preview.setSurfaceProvider( previewView.getSurfaceProvider()) }, ContextCompat.getMainExecutor(this)) }
Java
private ImageCapture imageCapture; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); PreviewView previewView = findViewById(R.id.previewView); ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this); cameraProviderFuture.addListener(() -> { try { // Camera provider is now guaranteed to be available ProcessCameraProvider cameraProvider = cameraProviderFuture.get(); // Set up the view finder use case to display camera preview Preview preview = new Preview.Builder().build(); // Set up the capture use case to allow users to take photos imageCapture = new ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build(); // Choose the camera by requiring a lens facing CameraSelector cameraSelector = new CameraSelector.Builder() .requireLensFacing(lensFacing) .build(); // Attach use cases to the camera with the same lifecycle owner Camera camera = cameraProvider.bindToLifecycle( ((LifecycleOwner) this), cameraSelector, preview, imageCapture); // Connect the preview use case to the previewView preview.setSurfaceProvider( previewView.getSurfaceProvider()); } catch (InterruptedException | ExecutionException e) { // Currently no exceptions thrown. cameraProviderFuture.get() // shouldn't block since the listener is being called, so no need to // handle InterruptedException. } }, ContextCompat.getMainExecutor(this)); }
Se garantiza la compatibilidad con las siguientes combinaciones de configuración (cuando se requiere vista previa o captura de video, pero no ambas al mismo tiempo):
Vista previa o captura de video | Captura de imágenes | Análisis | Descripciones |
---|---|---|---|
Brinda al usuario una vista previa o le permite grabar un video, tomar una foto y analizar la transmisión de imágenes. | |||
Toma una foto y analiza la transmisión de imágenes. | |||
Brinda al usuario una vista previa o le permite grabar un video y tomar una foto. | |||
Brinda al usuario una vista previa o le permite grabar un video y analizar la transmisión de imágenes. |
Cuando se requieren tanto la vista previa como la captura de video, se admiten de forma condicional las siguientes combinaciones de casos de uso:
Vista previa | Captura de video | Captura de imágenes | Análisis | Requisito especial |
---|---|---|---|---|
Garantizado para todas las cámaras | ||||
Dispositivo de cámara LIMITADO (o mejor) | ||||
Dispositivo de cámara NIVEL 3 (o mejor) |
Además:
- Todos los casos de uso pueden funcionar por su cuenta. Por ejemplo, una app puede grabar video sin usar la vista previa.
- Cuando se habilitan las extensiones, solo se garantiza que funcione la combinación
ImageCapture
yPreview
. En función de la implementación del OEM, es posible que tampoco se pueda agregarImageAnalysis
. No se pueden habilitar para el caso de uso deVideoCapture
. Para obtener más información, consulta el documento de referencia de extensiones. - En función de sus capacidades, algunas cámaras pueden admitir la combinación en modos de menor resolución, pero no pueden admitir la misma combinación en resoluciones más altas.
El nivel de hardware compatible se puede recuperar desde Camera2CameraInfo
. Por ejemplo, el siguiente código verifica si la cámara posterior predeterminada es un dispositivo LEVEL_3
:
Kotlin
@androidx.annotation.OptIn(ExperimentalCamera2Interop::class) fun isBackCameraLevel3Device(cameraProvider: ProcessCameraProvider) : Boolean { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return CameraSelector.DEFAULT_BACK_CAMERA .filter(cameraProvider.availableCameraInfos) .firstOrNull() ?.let { Camera2CameraInfo.from(it) } ?.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3 } return false }
Java
@androidx.annotation.OptIn(markerClass = ExperimentalCamera2Interop.class) Boolean isBackCameraLevel3Device(ProcessCameraProvider cameraProvider) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { List\filteredCameraInfos = CameraSelector.DEFAULT_BACK_CAMERA .filter(cameraProvider.getAvailableCameraInfos()); if (!filteredCameraInfos.isEmpty()) { return Objects.equals( Camera2CameraInfo.from(filteredCameraInfos.get(0)).getCameraCharacteristic( CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL), CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3); } } return false; }
Permisos
La app necesita el permiso CAMERA
. Para guardar imágenes en archivos, también necesitarás el permiso WRITE_EXTERNAL_STORAGE
, excepto en dispositivos que ejecuten Android 10 o versiones posteriores.
Si quieres obtener más información para configurar permisos en tu app, consulta Cómo solicitar permisos de app.
Requisitos
CameraX tiene los siguientes requisitos mínimos de versión:
- Nivel 21 de la API de Android
- Componentes de la arquitectura de Android 1.1.1
Para actividades relacionadas con el ciclo de vida, usa FragmentActivity
o AppCompatActivity
.
Cómo declarar dependencias
Para agregar una dependencia en CameraX, debes agregar el repositorio de Maven de Google a tu proyecto.
Abre el archivo settings.gradle
de tu proyecto y agrega el repositorio google()
como se muestra a continuación:
Groovy
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } }
Kotlin
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } }
Agrega lo siguiente al final del bloque de Android:
Groovy
android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } // For Kotlin projects kotlinOptions { jvmTarget = "1.8" } }
Kotlin
android { compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } // For Kotlin projects kotlinOptions { jvmTarget = "1.8" } }
Agrega lo siguiente al archivo build.gradle
de cada módulo para una app:
Groovy
dependencies { // CameraX core library using the camera2 implementation def camerax_version = "1.3.0-alpha07" // The following line is optional, as the core library is included indirectly by camera-camera2 implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}" // If you want to additionally use the CameraX Lifecycle library implementation "androidx.camera:camera-lifecycle:${camerax_version}" // If you want to additionally use the CameraX VideoCapture library implementation "androidx.camera:camera-video:${camerax_version}" // If you want to additionally use the CameraX View class implementation "androidx.camera:camera-view:${camerax_version}" // If you want to additionally add CameraX ML Kit Vision Integration implementation "androidx.camera:camera-mlkit-vision:${camerax_version}" // If you want to additionally use the CameraX Extensions library implementation "androidx.camera:camera-extensions:${camerax_version}" }
Kotlin
dependencies { // CameraX core library using the camera2 implementation val camerax_version = "1.3.0-alpha07" // The following line is optional, as the core library is included indirectly by camera-camera2 implementation("androidx.camera:camera-core:${camerax_version}") implementation("androidx.camera:camera-camera2:${camerax_version}") // If you want to additionally use the CameraX Lifecycle library implementation("androidx.camera:camera-lifecycle:${camerax_version}") // If you want to additionally use the CameraX VideoCapture library implementation("androidx.camera:camera-video:${camerax_version}") // If you want to additionally use the CameraX View class implementation("androidx.camera:camera-view:${camerax_version}") // If you want to additionally add CameraX ML Kit Vision Integration implementation("androidx.camera:camera-mlkit-vision:${camerax_version}") // If you want to additionally use the CameraX Extensions library implementation("androidx.camera:camera-extensions:${camerax_version}") }
Si quieres obtener más información para configurar tu app a fin de cumplir con estos requisitos, consulta Cómo declarar dependencias.
Recursos adicionales
Para obtener más información acerca de CameraX, consulta los siguientes recursos adicionales.
Codelab
Muestra de código