Esta página aborda a arquitetura do CameraX, incluindo a estrutura, como trabalhar com a API e os ciclos de vida e como combinar casos de uso.
Estrutura do CameraX
É possível usar o CameraX para interagir com a câmera de um dispositivo por uma abstração chamada de caso de uso. Os casos de uso abaixo estão disponíveis:
- Visualização: aceita uma superfície para exibir uma visualização, como uma
PreviewView
. - Análise de imagem: fornece buffers acessíveis por CPU para análise, por exemplo, para aprendizado de máquina.
- Captura de imagem: captura e salva uma foto.
- Captura de vídeo: faz uma captura de vídeo e áudio com
VideoCapture
.
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 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 casos de uso. Por
exemplo, o caso de uso de captura de imagem fornece uma chamada de método takePicture()
.
Em vez de um aplicativo posicionar chamadas 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
cameraProvider.bindToLifecycle()
.
O ciclo de vida informa o CameraX quando configurar a sessão de captura de câmera,
garantindo que o estado da câmera mude de maneira adequada para corresponder às transições do ciclo.
Para ver as etapas de implementação de cada caso de uso, consulte Implementar uma visualização, Analisar imagens, Tirar uma foto e Captura de vídeo.
Exemplo de modelo de API
O caso de uso da visualização interage com um
Surface
para exibição. Os aplicativos
usam o seguinte código para criar o caso de uso com opções de configuração:
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 mais exemplos de códigos, consulte o app de exemplo oficial do CameraX (link em inglês).
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 podem ser combinados, você poderá seguir um destes procedimentos:
- Agrupe os casos de uso compatíveis em mais de um fragmento e 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 de ciclos de vida 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 da CameraX usando
ProcessCameraProvider.unbindAll()
ou desvinculando cada um deles individualmente. Como alternativa, ao vincular casos
de uso a um ciclo de vida, você pode deixar que o CameraX
gerencie a abertura e o encerramento 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 com reconhecimento de 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 com reconhecimento 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.
O exemplo 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 lifecycleRegistry; public CustomLifecycle() { lifecycleRegistry = new LifecycleRegistry(this); lifecycleRegistry.markState(Lifecycle.State.CREATED); } ... public void doOnResume() { lifecycleRegistry.markState(State.RESUMED); } ... public Lifecycle getLifecycle() { return lifecycleRegistry; } }
Com esse LifecycleOwner
, seu app poderá posicionar transições de estado em
pontos específicos do código. Para ter mais informações sobre como implementar essa
funcionalidade no seu app, consulte Implementar um
LifecycleOwner personalizado.
Casos de usos simultâneos
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 com uma única chamada a
CameraProcessProvider.bindToLifecycle()
. Para ter mais informações sobre práticas recomendadas
em mudanças de configuração, consulte Gerenciar alterações
de configuração.
No exemplo 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
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)); }
As combinações de configuração a seguir têm garantia de compatibilidade (quando é necessário visualizar ou capturar vídeos, mas não ao mesmo tempo):
Visualização ou captura de vídeo | Captura de imagem | Análise | Descrições |
---|---|---|---|
Fornece ao usuário uma visualização ou grava um vídeo, tira uma foto e analisa o stream de imagem. | |||
Tira uma foto e analisa o fluxo da imagem. | |||
Fornece ao usuário uma visualização ou grava um vídeo e tira uma foto. | |||
Fornece uma visualização ou grava o vídeo e analisa o stream de imagem. |
Quando a visualização e a captura de vídeo são necessárias, as seguintes combinações de casos de uso são compatíveis condicionalmente:
Visualização | Captura de vídeo | Captura de imagem | Análise | Requisito especial |
---|---|---|---|---|
Garantido para todas as câmeras | ||||
Dispositivo de câmera LIMITED (ou superior). | ||||
Dispositivo de câmera LEVEL_3 (ou superior). |
Além disso:
- Cada caso de uso funciona sozinho. Por exemplo, um app pode gravar vídeos sem usar uma visualização.
- Quando as extensões estão ativadas, apenas a combinação
ImageCapture
ePreview
tem garantia de funcionar. Dependendo da implementação do OEM, talvez não seja possível adicionarImageAnalysis
. As extensões não podem ser ativadas no caso de uso deVideoCapture
. Consulte o documento de referência de extensão para saber mais. - Dependendo da capacidade, algumas câmeras oferecem suporte para a combinação em modos de resolução mais baixa, mas não em uma resolução mais alta.
O nível de hardware compatível
pode ser recuperado de Camera2CameraInfo
. Por exemplo, o código a seguir
verifica se a câmera traseira padrão é um 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; }
Permissões
Seu app precisará da
permissão CAMERA
. Para
salvar imagens em arquivos, ele também precisa da permissão
WRITE_EXTERNAL_STORAGE
,
exceto em dispositivos com Android 10 ou mais recente.
Para ter mais informações sobre como configurar permissões para seu app, leia Solicitar permissões do app.
Requisitos
O CameraX possui os seguintes requisitos mínimos de versão:
- Nível 21 da API do Android
- 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, é preciso adicionar o repositório Maven do Google ao projeto.
Abra o arquivo settings.gradle
do projeto e adicione o repositório google()
,
conforme mostrado a seguir.
Groovy
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } }
Kotlin
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } }
Adicione o código abaixo ao fim do bloco do 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" } }
Adicione o código a seguir ao arquivo build.gradle
de cada módulo para um app:
Groovy
dependencies { // CameraX core library using the camera2 implementation def camerax_version = "1.3.0-alpha04" // 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-alpha04" // 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}") }
Para ter 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.
Codelab
Exemplo de código