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

Arquitetura do CameraX

O CameraX é uma adição ao Jetpack que facilita a utilização dos recursos das APIs Camera2. Este tópico aborda a arquitetura do CameraX, incluindo a estrutura, como trabalhar com a API e com Lifecycles e como combinar casos de uso.

Estrutura do CameraX

Os desenvolvedores usam o CameraX para interagir com a câmera de um dispositivo por meio de uma abstração chamada de caso de uso. No momento, estes são os casos de uso disponíveis:

  • Visualização: prepara uma visualização SurfaceTexture..
  • Análise de imagem: fornece buffers acessíveis por CPU para análise, como para aprendizado de máquina.
  • Captura de imagem: captura e salva uma foto.

Os casos de uso podem ser combinados e ativados simultaneamente. Por exemplo, um app pode permitir que o usuário visualize a imagem que a câmera vê usando um caso de uso de visualização, ter um caso de uso de análise de imagem que determina se as pessoas na foto estão sorrindo e incluir um caso de uso de captura de imagem para tirar uma foto quando elas sorrirem.

Modelo de API

Para trabalhar com a biblioteca, especifique o seguinte:

  • O caso de uso desejado com opções de configuração.
  • O que fazer com os dados de saída ao anexar listeners.
  • O fluxo pretendido, por exemplo, quando ativar câmeras e produzir dados ao vincular o caso de uso ao Android Architecture Lifecycles.

Os objetos de configuração para casos de uso são configurados com métodos set() e finalizados com o método build(). Cada objeto de caso de uso fornece um conjunto de APIs específicas ao caso de uso. Por exemplo, a captura de imagem fornece uma chamada de método takePicture().

Em vez de um aplicativo posicionar chamadas de método específicas de início e parada em onResume() e onPause(), o aplicativo especifica um ciclo de vida ao qual a câmera será associada, usando CameraX.bindToLifecycle(). A inicialização, o encerramento e a produção de dados são regidos pelo Android Architecture Lifecycle especificado. depara configurar a sessão de captura da câmera, garantindo que o estado da câmera seja alterado adequadamente para corresponder às transições do ciclo.

Para ver as etapas de implementação de cada caso de uso, consulte Implementar visualização, Analisar imagens e Tirar uma foto.

Exemplo de modelo de API

O caso de uso de visualização apresenta uma SurfaceTexture para exibição. Os aplicativos usam o código a seguir para criar o caso de uso com opções de configuração:

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 mais exemplos de códigos, consulte o app de amostra oficial do CameraX.

Lifecycles do CameraX

O CameraX leva em conta um ciclo de vida para determinar quando abrir a câmera, quando criar uma sessão de captura e quando parar tudo e encerrar. As APIs de caso de uso fornecem chamadas de método e callbacks para monitorar o progresso.

Conforme explicado em Combinar casos de uso, é possível vincular algumas combinações de casos de uso a um único ciclo de vida. Quando seu app tiver que ser compatível com casos de uso que não possam ser combinados, você poderá seguir um destes procedimentos:

  • Agrupe os casos de uso compatíveis em mais de um fragmento, depois alterne entre os fragmentos.
  • Crie um componente de ciclo de vida personalizado e use-o para controlar manualmente o ciclo de vida da câmera.

Se você desacoplar os proprietários do Lifecycle dos seus casos de uso de visualização e câmera (por exemplo, ao usar um ciclo de vida personalizado ou um fragmento de retenção), será necessário garantir que todos os casos de uso estejam desvinculados do CameraX por meio de CameraX.unbindAll() ou desvincular cada um deles individualmente. Como alternativa, ao vincular casos de uso ao método onCreate para conseguir um Lifecycle padrão, você pode deixar que o CameraX gerencie a abertura e o fechamento da sessão de captura e a desvinculação dos casos de uso.

Se toda a funcionalidade da sua câmera corresponder ao ciclo de vida de um único componente compatível com ciclo de vida, como um fragmento AppCompatActivity ou AppCompat, usar o ciclo de vida desse componente ao vincular todos os casos de uso garantirá que a funcionalidade de câmera esteja pronta quando o componente de ciclo de vida estiver ativo e que, caso contrário, seja descartada com segurança, sem consumir recursos.

LifecycleOwners personalizados

Em casos avançados, é possível criar um LifecycleOwner personalizado para permitir que seu app controle explicitamente o ciclo de vida da sessão do CameraX em vez de vinculá-lo a um LifecycleOwner padrão do Android.

A amostra de código a seguir mostra como criar um LifecycleOwner personalizado simples:

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;
        }
    }
    

Com esse LifecycleOwner, seu app poderá posicionar transições de estado em pontos específicos do código. Para saber mais informações sobre como implementar essa funcionalidade no seu app, consulte Implementar um LifecycleOwner personalizado.

Combinar casos de uso

Casos de uso podem ser executados simultaneamente. Embora eles possam ser vinculados de modo sequencial a um ciclo de vida, é melhor vincular todos os casos de uso a única chamada a CameraX.bindToLifecycle(). Para saber mais informações sobre práticas recomendadas em alterações de configuração, consulte Gerenciar alterações de configuração.

Na amostra de código a seguir, o app especifica os dois casos de uso que serão criados e executados simultaneamente. Também determina o ciclo de vida a ser usado nos dois casos de uso, para que ambos possam ser iniciados e encerrados de acordo com esse ciclo.

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);
    }
    

As seguintes combinações de configuração são compatíveis:

Análise Visualização Captura de imagem Combinação de casos de usos
Fornece uma visualização ao usuário, tira uma foto e analisa o fluxo da imagem.
Tira uma foto e analisa o fluxo da imagem.
Fornece uma visualização com efeitos visuais aplicados com base na análise das imagens em exibição.
Mostra o que a câmera vê e tira uma foto quando o usuário solicita.

Permissões

Seu app precisará da permissão CAMERA. Para salvar imagens em arquivos, ele precisará também da permissão WRITE_EXTERNAL_STORAGE, exceto em dispositivos com Android Q ou posterior.

Para saber mais informações sobre como configurar permissões para seu app, leia Solicitar permissões de app.

Requisitos

O CameraX possui os seguintes requisitos mínimos de versão:

  • API Android de nível 21
  • Android Architecture Components 1.1.1

Para atividades que envolvam ciclo de vida, use FragmentActivity ou AppCompatActivity.

Declarar dependências

Para adicionar uma dependência ao CameraX, você precisará adicionar o repositório do Google Maven ao seu projeto.

Abra o arquivo build.gradle do seu projeto e adicione o repositório google(), conforme mostrado a seguir:

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

Adicione o seguinte código ao arquivo build.gradle de cada 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 saber mais informações sobre como configurar seu app para atender a esses requisitos, consulte Declarar dependências.

Outros recursos

Para saber mais sobre o CameraX, consulte os seguintes recursos adicionais.

Codelabs

  • Introdução ao CameraX
  • Adicionar um fragmento CameraView ao seu app
  • Amostra de código

  • App de amostra oficial do CameraX