Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Arquitectura de CameraX

CameraX es un agregado de Jetpack que permite aprovechar las funciones de las API de Camera2 fácilmente. En este tema 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 prácticos.

Estructura de CameraX

Los desarrolladores usan CameraX para interactuar con la cámara del dispositivo mediante una generalización llamada caso práctico. Actualmente, se encuentran disponibles los siguientes casos prácticos:

  • Vista previa: Acepta una superficie 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.

Los casos prácticos pueden combinarse y estar activos en simultáneo. Por ejemplo, una app puede permitir que el usuario vea lo que ve la cámara con un caso práctico de vista previa, usar otro de análisis de imágenes para determinar si las personas que aparecen en la foto están sonriendo, y también incluir otro más para la 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
  • El flujo pretendido, como cuándo habilitar las cámaras y cuándo producir datos, mediante la vinculación del caso práctico en los ciclos de vida de la arquitectura de Android

Puedes configurar casos prácticos 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().

En lugar de que una aplicación coloque llamadas de método de inicio y finalización en onResume() y onPause(), especificará un ciclo de vida para asociar la cámara, para lo cual usará cameraProvider.bindToLifecycle(). Ese 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 coincidir con las transmisiones del ciclo de vida.

Si quieres conocer los pasos de implementación para cada caso práctico, consulta Cómo implementar una vista previa, Cómo analizar imágenes y Captura de imágenes.

Ejemplo de modelo de API

El caso práctico de la vista previa interactúa con un elemento Surface para mostrarla. Las aplicaciones crean el caso práctico con opciones de configuración y usan 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.createSurfaceProvider(camera.cameraInfo))
    

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.createSurfaceProvider(camera.getCameraInfo());
    

Para ver más ejemplos de código, consulta la app de ejemplo oficial de CameraX.

Ciclos de vida de CameraX

CameraX obedece un ciclo de vida para determinar cuándo debe abrir la cámara, crear una sesión de captura, detenerse y cerrarse. Las API de casos prácticos proporcionan llamadas y devoluciones de llamada de métodos con el fin de supervisar el progreso.

Como se explica en Combina casos prácticos, puedes vincular combinaciones de casos prácticos en un solo ciclo de vida. Cuando tu app necesite admitir casos prácticos que no se puedan combinar, puedes hacer una de las siguientes tareas:

  • Agrupar casos prácticos compatibles en más de un fragmento y, luego, intercambiar entre los fragmentos
  • Crear un componente del ciclo de vida personalizado y usarlo para controlar el ciclo de vida de la cámara en forma manual

Si desacoplas la vista y los propietarios de los ciclos de vida de los casos prácticos (por ejemplo, si usas un ciclo de vida personalizado o conservar fragmento), debes asegurarte de que todos los casos prácticos estén desvinculados de CameraX; para ello, utiliza CameraX.unbindAll() o desvincula cada caso de uso de forma individual. De manera alternativa, cuando vinculas casos prácticos 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 prácticos.

Si toda la funcionalidad de tu cámara corresponde al ciclo de vida de un solo componente que tiene en cuenta los ciclos de vida, como un fragmento AppCompatActivity o AppCompat, el uso del ciclo de vida de ese componente cuando vincules todos los casos prácticos deseados garantizará que la funcionalidad de la cámara esté lista cuando el componente esté activo y que se descarte de manera segura y sin consumir recursos.

LifecycleOwners 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. Para obtener más información sobre cómo implementar esta funcionalidad en tu app, consulta Cómo implementar un LifecycleOwner personalizado.

Casos prácticos simultáneos

Los casos prácticos pueden ejecutarse en simultáneo. Si bien pueden estar vinculados de manera secuencial a un ciclo de vida, se recomienda vincular todos los casos prácticos con una sola llamada a CameraProcessProvider.bindToLifecycle(). Obtén más información sobre las recomendaciones para los cambios de configuración en Cómo administrar los cambios en la configuración.

En la siguiente muestra de código, la app especifica los dos casos prácticos que se crearán y ejecutarán simultáneamente. También especifica el ciclo de vida que se usará para ambos casos práctico, de manera 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.createSurfaceProvider(camera.cameraInfo))
        }, 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 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.createSurfaceProvider(camera.getCameraInfo()));
            } catch (InterruptedException | ExecutionException e) {
                // Currently no exceptions thrown. cameraProviderFuture.get() should
                // not block since the listener is being called, so no need to
                // handle InterruptedException.
            }
        }, ContextCompat.getMainExecutor(this));
    }
    

Se admiten las siguientes combinaciones de configuración:

Vista previa Análisis Captura de imágenes Combinación de casos prácticos
Proporciona una vista previa al usuario, permite tomar una foto y analiza la transmisión de imágenes.
  Toma una foto y analiza la transmisión de imágenes.
  Proporciona una vista previa con efectos visuales aplicados según el análisis de las imágenes que se transmiten.
  Muestra la vista de la cámara y toma una foto con la acción del usuario.

Cuando se habilitan las extensiones, solo ImageCapture y Preview están garantizadas. En función de la implementación del OEM, es posible que tampoco se pueda usar ImageAnalysis.

ImageCapture no funciona por sí solo, aunque Preview y ImageAnalysis sí lo hacen.

Permisos

Tu app necesita el permiso CAMERA. Para guardar imágenes en archivos, también necesitarás el permiso WRITE_EXTERNAL_STORAGE, excepto en dispositivos con Android 10 o versiones posteriores.

Para obtener más información acerca de cómo 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 útil, 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 build.gradle de tu proyecto y agrega el repositorio google() como se muestra a continuación:

    allprojects {
        repositories {
            google()
            jcenter()
        }
    }
    

Agrega lo siguiente al final del bloque de Android:

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    

Agrega lo siguiente al archivo build.gradle de cada módulo para una app:

dependencies {
  // CameraX core library using the camera2 implementation
  def camerax_version = "1.0.0-beta08"
  // 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 View class
  implementation "androidx.camera:camera-view:1.0.0-alpha15"
  // If you want to additionally use the CameraX Extensions library
  implementation "androidx.camera:camera-extensions:1.0.0-alpha15"
}

Para obtener más información sobre cómo configurar tu app a fin de cumplir con estos requisitos, consulta Declaración de dependencias.

Recursos adicionales

Para obtener más información acerca de CameraX, consulta los siguientes recursos adicionales.

Codelab

  • Cómo comenzar a usar CameraX
  • Muestra de código

  • App de ejemplo oficial de CameraX