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.
Há duas maneiras de criar um app do CameraX: um
CameraController
(excelente se
você quiser a maneira mais simples de usar o CameraX) ou um
CameraProvider
(ótimo se você
precisar de mais flexibilidade).
CameraController
Um CameraController
fornece a maioria das funcionalidades principais do CameraX em uma única
classe. Ele requer pouco código de configuração e processa automaticamente a inicialização da
câmera, o gerenciamento de casos de uso, a rotação desejada, o toque para focar,
o gesto de pinça para aplicar zoom e muito mais. A classe concreta que estende CameraController
é
LifecycleCameraController
.
Kotlin
val previewView: PreviewView = viewBinding.previewView var cameraController = LifecycleCameraController(baseContext) cameraController.bindToLifecycle(this) cameraController.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA previewView.controller = cameraController
Java
PreviewView previewView = viewBinding.previewView; LifecycleCameraController cameraController = new LifecycleCameraController(baseContext); cameraController.bindToLifecycle(this); cameraController.setCameraSelector(CameraSelector.DEFAULT_BACK_CAMERA); previewView.setController(cameraController);
Os UseCase
s padrão do CameraController
são Preview
, ImageCapture
e ImageAnalysis
. Para desativar ImageCapture
ou ImageAnalysis
ou ativar
VideoCapture
, use o
método
setEnabledUseCases()
.
Para conferir mais usos de CameraController
, consulte o
exemplo do leitor de QR code
ou o
vídeo de noções básicas do CameraController
(links em inglês).
CameraProvider
Um CameraProvider
ainda é fácil de usar, mas como o desenvolvedor do app processa
mais da configuração, há mais oportunidades para personalizá-la,
como ativar a rotação da imagem de saída ou definir o formato da imagem de saída em
ImageAnalysis
. Você também pode usar uma Surface
personalizada para a visualização da câmera, o que proporciona
mais flexibilidade. Já com o CameraController é necessário usar uma
PreviewView
. Usar o código da Surface
já existente pode ser útil se ele
já for uma entrada para outras partes do app.
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 conferir 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.
O caso de uso da visualização interage com uma
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)); }
O CameraX permite o uso simultâneo de uma instância de Preview
,
VideoCapture
, ImageAnalysis
e ImageCapture
. 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.
- Em dispositivos com nível de hardware da câmera
FULL
ou inferior, a combinação dePreview
,VideoCapture
eImageCapture
ouImageAnalysis
pode forçar o CameraX a duplicar o fluxoPRIV
da câmera paraPreview
eVideoCapture
. Essa duplicação, chamada de compartilhamento de stream, permite o uso simultâneo desses recursos, mas tem o custo de aumentar as demandas de processamento. Como resultado, a latência pode ser um pouco maior e a duração da bateria pode ser reduzida.
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\<CameraInfo\> 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.5.0-alpha03" // 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.5.0-alpha03" // 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.
Interoperabilidade do CameraX com o Camera2
O CameraX foi desenvolvido com base no Camera2. Ele permite a leitura e até mesmo a gravação de propriedades na implementação do Camera2. Para mais detalhes, consulte o pacote de interoperabilidade.
Para mais informações sobre como o CameraX configurou as propriedades do Camera2, use
Camera2CameraInfo
para ler o CameraCharacteristics
subjacente. Também é possível gravar as propriedades subjacentes do Camera2
de uma das duas formas a seguir:
Use
Camera2CameraControl
, que permite definir propriedades noCaptureRequest
subjacente, como o modo de autofoco.Estenda um CameraX
UseCase
com umCamera2Interop.Extender
. Isso permite definir propriedades no CaptureRequest, comoCamera2CameraControl
. Ele também oferece alguns outros controles, como configurar o caso de uso de stream para otimizar a câmera para seu cenário de uso. Para mais informações, consulte Usar casos de uso de streaming para melhorar a performance.
O exemplo de código a seguir usa casos de uso de stream para otimizar uma videochamada.
Use Camera2CameraInfo
para buscar se o caso de uso de stream de videochamada está disponível. Em seguida, use um
Camera2Interop.Extender
para definir o caso de uso do stream subjacente.
Kotlin
// Set underlying Camera2 stream use case to optimize for video calls. val videoCallStreamId = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL.toLong() // Check available CameraInfos to find the first one that supports // the video call stream use case. val frontCameraInfo = cameraProvider.getAvailableCameraInfos() .first { cameraInfo -> val isVideoCallStreamingSupported = Camera2CameraInfo.from(cameraInfo) .getCameraCharacteristic( CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES )?.contains(videoCallStreamId) val isFrontFacing = (cameraInfo.getLensFacing() == CameraSelector.LENS_FACING_FRONT) (isVideoCallStreamingSupported == true) && isFrontFacing } val cameraSelector = frontCameraInfo.cameraSelector // Start with a Preview Builder. val previewBuilder = Preview.Builder() .setTargetAspectRatio(screenAspectRatio) .setTargetRotation(rotation) // Use Camera2Interop.Extender to set the video call stream use case. Camera2Interop.Extender(previewBuilder).setStreamUseCase(videoCallStreamId) // Bind the Preview UseCase and the corresponding CameraSelector. val preview = previewBuilder.build() camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)
Java
// Set underlying Camera2 stream use case to optimize for video calls. Long videoCallStreamId = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL.toLong(); // Check available CameraInfos to find the first one that supports // the video call stream use case. List<CameraInfo> cameraInfos = cameraProvider.getAvailableCameraInfos(); CameraInfo frontCameraInfo = null; for (cameraInfo in cameraInfos) { Long[] availableStreamUseCases = Camera2CameraInfo.from(cameraInfo) .getCameraCharacteristic( CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES ); boolean isVideoCallStreamingSupported = Arrays.List(availableStreamUseCases) .contains(videoCallStreamId); boolean isFrontFacing = (cameraInfo.getLensFacing() == CameraSelector.LENS_FACING_FRONT); if (isVideoCallStreamingSupported && isFrontFacing) { frontCameraInfo = cameraInfo; } } if (frontCameraInfo == null) { // Handle case where video call streaming is not supported. } CameraSelector cameraSelector = frontCameraInfo.getCameraSelector(); // Start with a Preview Builder. Preview.Builder previewBuilder = Preview.Builder() .setTargetAspectRatio(screenAspectRatio) .setTargetRotation(rotation); // Use Camera2Interop.Extender to set the video call stream use case. Camera2Interop.Extender(previewBuilder).setStreamUseCase(videoCallStreamId); // Bind the Preview UseCase and the corresponding CameraSelector. Preview preview = previewBuilder.build() Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)
Outros recursos
Para saber mais sobre o CameraX, consulte os recursos a seguir.
Codelab
Exemplo de código