Android Dev Summit, October 23-24: two days of technical content, directly from the Android team. Sign-up for livestream updates.

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, incluida la estructura, cómo trabajar con la API, cómo trabajar 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: Prepara una vista previa de SurfaceTexture.
  • 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, tener un caso práctico de análisis de imágenes que determine si las personas que aparecen en la foto están sonriendo y también incluir un caso práctico 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
  • 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

Los objetos de configuración para los casos prácticos se configuran con métodos set() y finalizan con el método build(). Cada objeto de caso práctico proporciona un conjunto de API específicas de casos prácticos. Por ejemplo, el caso práctico de captura de imágenes proporciona una llamada de 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, mediante CameraX.bindToLifecycle(). El ciclo de vida de la arquitectura de Android especificado controla el inicio, el cierre y la producción de datos. 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.

Para conocer los pasos de implementación de cada caso práctico, consulta Cómo implementar una vista previa, Cómo analizar imágenes y Cómo tomar una foto.

Ejemplo de modelo de API

El caso práctico de la vista previa proporciona una SurfaceTexture para mostrar. Las aplicaciones crean el caso práctico con opciones de configuración y usan el siguiente código:

Kotlin

    val previewConfig = PreviewConfig.Builder().build()
    val preview = Preview(previewConfig)

    val textureView: TextureView = findViewById(R.id.textureView)

    // The output data-handling is configured in a listener.
    preview.setOnPreviewOutputUpdateListener { previewOutput ->
        textureView.surfaceTexture = previewOutput.surfaceTexture
    }

    // The use case is bound to an Android Lifecycle with the following code.
    CameraX.bindToLifecycle(this as LifecycleOwner, preview)
    

Java

    PreviewConfig config = new PreviewConfig.Builder().build();
    Preview preview = new Preview(config);

    TextureView textureView = findViewById(R.id.textureView);

    preview.setOnPreviewOutputUpdateListener(
        new Preview.OnPreviewOutputUpdateListener() {
            @Override
            public void onUpdated(Preview.PreviewOutput previewOutput) {
                // The output data-handling is configured in a listener.
                textureView.setSurfaceTexture(previewOutput.getSurfaceTexture());
                // Your custom code here.
            });
    });

    // The use case is bound to an Android Lifecycle with the following code.
    CameraX.bindToLifecycle((LifecycleOwner) this, preview);
    

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 debe abrir la cámara, crear una sesión de captura y, 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 personalizado 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 conservas un fragmento), debes asegurarte de que todos los casos prácticos estén desvinculados de CameraX con CameraX.unbindAll() o desvinculando cada caso práctico individualmente. De manera alternativa, cuando vinculas casos prácticos en el método onCreate para un ciclo de vida estándar, puedes dejar 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, usar el ciclo de vida de ese componente al vincular todos los casos prácticos deseados garantizará que, de cualquier otra manera, la funcionalidad de la cámara esté lista cuando el componente esté activo, y se descarte de manera segura, sin consumir recursos.

LifecycleOwners personalizado

Puedes crear un LifecycleOwner personalizado para casos avanzados a fin de que tu app pueda controlar de manera explícita el ciclo de vida de la sesión de CameraX, en lugar de unirlo a un LifecycleOwner de Android estándar.

En el siguiente código, se muestra 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 mLifecycleRegistry;
        public CustomLifecycle() {
            mLifecycleRegistry = new LifecycleRegistry(this);
            mLifecycleRegistry.markState(Lifecycle.State.CREATED);
        }
       ...
       public void doOnResume() {
            mLifecycleRegistry.markState(State.RESUMED);
        }
       ...
        public Lifecycle getLifecycle() {
            return mLifecycleRegistry;
        }
    }
    

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.

Combina casos prácticos

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 CameraX.bindToLifecycle(). Obtén más información sobre las recomendaciones para los cambios de configuración en Administra los cambios de configuración.

En el siguiente ejemplo 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

    val imageCapture: ImageCapture

    override fun onCreate() {
        val previewConfig = PreviewConfig.Builder().build()
        val imageCaptureConfig = ImageCaptureConfiguration.Builder().build()

        val imageCapture = ImageCapture(imageCaptureConfig)
        val preview = Preview(previewConfig)

        val textureView = findViewById(R.id.textureView)

        preview.setOnPreviewOutputUpdateListener { previewOutput ->
            textureView.surfaceTexture = previewOutput.surfaceTexture
        }

        CameraX.bindToLifecycle(this as LifecycleOwner, preview, imageCapture)
    }
    

Java

    private ImageCapture imageCapture;

    void onCreate() {
        PreviewConfig previewConfig = new PreviewConfig.Builder().build();
        imageCaptureConfig imageCaptureConfig =
            new ImageCaptureConfig.Builder().build();

        imageCapture = new ImageCapture(imageCaptureConfig);
        preview = new Preview(previewConfig);

        TextureView textureView = findViewById(R.id.textureView);

        preview.setOnPreviewOutputUpdateListener(
            previewOutput -> {
                textureView.setSurfaceTexture(previewOutput.getSurfaceTexture());
        });

        CameraX.bindToLifecycle((LifecycleOwner) this, preview, imageCapture);
    }
    

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 lo que ve la cámara y toma una foto de la acción del usuario.

Permisos

Tu app necesita el permiso CAMERA. Para guardar imágenes, también necesitará el permiso WRITE_EXTERNAL_STORAGE, salvo en dispositivos que ejecutan Android Q 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 Android 1.1.1

Para actividades que tienen en cuenta el ciclo de vida, usa FragmentActivity o AppCompatActivity.

Cómo declarar dependencias

Para agregar una dependencia en CameraX, debes agregar el repositorio de Google Maven a tu proyecto.

Abre el archivo build.gradle para tu proyecto y agrega el repositorio google(), como se muestra en el siguiente ejemplo:

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

Agrega lo siguiente a cada archivo build.gradle del módulo:

    dependencies {
        // CameraX core library.
        def camerax_version = "1.0.0-alpha01"
        implementation "androidx.camera.core:core:$camerax_version"
        // If you want to use CameraView.
        implementation "androidx.camera.core:view:$camerax_version"
        // If you want to use Camera2 extensions.
        implementation "androidx.camera.core:camera2:$camerax_version"
    }
    

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.

Codelabs

  • Cómo comenzar a usar CameraX
  • Cómo agregar un fragmento de CameraView a tu app
  • Muestra de código

  • App de muestra de CameraX oficial