Questa pagina descrive l'architettura di CameraX, inclusi la struttura, come utilizzare l'API, come utilizzare i cicli di vita e come combinare i casi d'uso.
Struttura di CameraX
Puoi utilizzare CameraX per interfacciarti con la fotocamera di un dispositivo tramite un'astrazione chiamata caso d'uso. Sono disponibili i seguenti casi d'uso:
- Anteprima: accetta una superficie per la visualizzazione di un'anteprima, ad esempio un
PreviewView
. - Analisi delle immagini: fornisce buffer accessibili alla CPU per l'analisi, ad esempio per il machine learning.
- Acquisizione immagine: acquisisce e salva una foto.
- Acquisizione video: acquisisci video e audio con
VideoCapture
I casi d'uso possono essere combinati e attivi contemporaneamente. Ad esempio, un'app può consentire all'utente di visualizzare l'immagine che la videocamera vede utilizzando un caso d'uso di anteprima, avere un caso d'uso di analisi delle immagini che determina se le persone nella foto stanno sorridendo e includere un caso d'uso di acquisizione delle immagini per scattare una foto una volta che lo fanno.
Modello API
Per utilizzare la libreria, devi specificare quanto segue:
- Il caso d'uso desiderato con le opzioni di configurazione.
- Cosa fare con i dati di output collegando i listener.
- Il flusso previsto, ad esempio quando attivare le videocamere e quando produrre dati, associando lo scenario d'uso ai cicli di vita dell'architettura Android.
Esistono due modi per scrivere un'app CameraX: un'app basata su Intent (ideale se vuoi il modo più semplice per utilizzare CameraX) o un'app basata su Session (ideale se hai bisogno di maggiore flessibilità).CameraController
CameraProvider
CameraController
Un CameraController
fornisce la maggior parte delle funzionalità di base di CameraX in una singola classe. Richiede poco codice di configurazione e gestisce automaticamente l'inizializzazione della videocamera, la gestione dei casi d'uso, la rotazione del target, la messa a fuoco con tocco, lo zoom con pizzico e altro ancora. La classe concreta che 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);
I UseCase
predefiniti per CameraController
sono Preview
, ImageCapture
e
ImageAnalysis
. Per disattivare ImageCapture
o ImageAnalysis
o per attivare VideoCapture
, utilizza il metodo setEnabledUseCases()
.
Per altri utilizzi di CameraController
, consulta l'esempio di
scanner di codici QR
o il
video sulle nozioni di base di CameraController
.
CameraProvider
Un CameraProvider
è comunque facile da usare, ma poiché lo sviluppatore dell'app gestisce
gran parte della configurazione, ci sono più opportunità per personalizzare la configurazione,
come l'attivazione della rotazione dell'immagine di output o l'impostazione del formato dell'immagine di output in
ImageAnalysis
. Puoi anche utilizzare un Surface
personalizzato per l'anteprima della videocamera, il che offre maggiore flessibilità, mentre con CameraController devi utilizzare un PreviewView
. L'utilizzo del codice Surface
esistente potrebbe essere utile se
è già un input per altre parti dell'app.
Configura i casi d'uso utilizzando i metodi set()
e finalizzali con il metodo build()
. Ogni oggetto caso d'uso fornisce un insieme di API specifiche per il caso d'uso. Ad esempio, il caso d'uso di acquisizione delle immagini fornisce una chiamata al metodo takePicture()
.
Anziché inserire chiamate di metodi di avvio e arresto specifici in
onResume()
e onPause()
, l'applicazione specifica un ciclo di vita a cui associare
la videocamera, utilizzando
cameraProvider.bindToLifecycle()
.
Questo ciclo di vita indica a CameraX quando configurare la sessione di acquisizione della videocamera
e garantisce che lo stato della videocamera cambi in modo appropriato in base alle transizioni del ciclo di vita.
Per i passaggi di implementazione per ogni caso d'uso, consulta Implementare un'anteprima, Analizzare le immagini, Acquisizione di immagini e Acquisizione di video.
Il caso d'uso dell'anteprima interagisce con un
Surface
per la visualizzazione. Le applicazioni
creano lo scenario d'uso con le opzioni di configurazione utilizzando il seguente codice:
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();
Per altri esempi di codice, consulta l'app di esempio CameraX ufficiale.
Cicli di vita di CameraX
CameraX osserva un ciclo di vita per determinare quando aprire la fotocamera, quando creare una sessione di acquisizione e quando interrompere e chiudere. Le API per casi d'uso forniscono chiamate di metodi e callback per monitorare l'avanzamento.
Come spiegato in Combinare casi d'uso, puoi associare alcune combinazioni di casi d'uso a un singolo ciclo di vita. Quando la tua app deve supportare casi d'uso che non possono essere combinati, puoi:
- Raggruppa i casi d'uso compatibili in più di un frammento e poi passa da un frammento all'altro.
- Crea un componente del ciclo di vita personalizzato e utilizzalo per controllare manualmente il ciclo di vita della videocamera
Se separi i proprietari del ciclo di vita dei casi d'uso di visualizzazione e fotocamera (ad esempio, se utilizzi un ciclo di vita personalizzato o un frammento retain), devi assicurarti che tutti i casi d'uso siano scollegati da CameraX utilizzando ProcessCameraProvider.unbindAll()
o scollegando ogni caso d'uso singolarmente. In alternativa, quando associ casi d'uso a un ciclo di vita, puoi lasciare che CameraX gestisca l'apertura e la chiusura della sessione di acquisizione e l'annullamento dell'associazione dei casi d'uso.
Se tutte le funzionalità della videocamera corrispondono al ciclo di vita di un singolo componente sensibile al ciclo di vita, ad esempio un AppCompatActivity
o un frammento AppCompat
, l'utilizzo del ciclo di vita di questo componente durante il binding di tutti gli use case desiderati garantisce che la funzionalità della videocamera sia pronta quando il componente sensibile al ciclo di vita è attivo e che venga eliminata in modo sicuro, senza consumare risorse, altrimenti.
Custom LifecycleOwners
Per i casi avanzati, puoi creare un LifecycleOwner
personalizzato per consentire alla tua app di controllare esplicitamente il ciclo di vita della sessione CameraX anziché associarlo a un LifecycleOwner
Android standard.
Il seguente esempio di codice mostra come creare un semplice LifecycleOwner personalizzato:
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; } }
Utilizzando questo LifecycleOwner
, la tua app può inserire transizioni di stato nei punti desiderati del codice. Per saperne di più sull'implementazione di questa funzionalità nella tua app,
consulta Implementazione di un
LifecycleOwner personalizzato.
Casi d'uso simultanei
I casi d'uso possono essere eseguiti contemporaneamente. Sebbene i casi d'uso possano essere associati in sequenza a un ciclo di vita, è meglio associarli tutti con una singola chiamata a CameraProcessProvider.bindToLifecycle()
. Per ulteriori informazioni sulle best practice per le modifiche alla configurazione, consulta Gestire le modifiche alla configurazione.
Nel seguente esempio di codice, l'app specifica i due casi d'uso da creare ed eseguire contemporaneamente. Specifica anche il ciclo di vita da utilizzare per entrambi i casi d'uso, in modo che entrambi inizino e si arrestino in base al ciclo di vita.
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)); }
CameraX consente l'utilizzo simultaneo di un'istanza di Preview
,
VideoCapture
, ImageAnalysis
e ImageCapture
. Inoltre,
- Ogni caso d'uso può funzionare in modo indipendente. Ad esempio, un'app può registrare video senza utilizzare l'anteprima.
- Quando le estensioni sono attive, è garantito il funzionamento solo della combinazione
ImageCapture
ePreview
. A seconda dell'implementazione OEM, potrebbe non essere possibile aggiungere ancheImageAnalysis
; le estensioni non possono essere attivate per lo scenario d'usoVideoCapture
. Per ulteriori dettagli, consulta la documentazione di riferimento delle estensioni. - A seconda delle funzionalità della videocamera, alcune videocamere potrebbero supportare la combinazione in modalità a risoluzione inferiore, ma non la stessa combinazione ad alcune risoluzioni più elevate.
- Sui dispositivi con livello hardware della fotocamera
FULL
o inferiore, la combinazione diPreview
,VideoCapture
eImageCapture
oImageAnalysis
potrebbe forzare CameraX a duplicare lo streamPRIV
della fotocamera perPreview
eVideoCapture
. Questa duplicazione, chiamata condivisione di stream, consente l'utilizzo simultaneo di queste funzionalità, ma a costo di maggiori richieste di elaborazione. Di conseguenza, potresti riscontrare una latenza leggermente superiore e una durata della batteria ridotta.
Il livello hardware supportato
può essere recuperato da Camera2CameraInfo
. Ad esempio, il seguente codice
verifica se la fotocamera posteriore predefinita è un 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; }
Autorizzazioni
La tua app avrà bisogno dell'autorizzazione
CAMERA
. Per
salvare le immagini nei file, sarà necessaria anche l'autorizzazione
WRITE_EXTERNAL_STORAGE
, tranne sui dispositivi con Android 10 o versioni successive.
Per ulteriori informazioni sulla configurazione delle autorizzazioni per la tua app, leggi Richiedere autorizzazioni app.
Requisiti
CameraX ha i seguenti requisiti minimi di versione:
- Livello API Android 21
- Componenti dell'architettura Android 1.1.1
Per le attività sensibili al ciclo di vita, utilizza
FragmentActivity
o
AppCompatActivity
.
Dichiarare le dipendenze
Per aggiungere una dipendenza da CameraX, devi aggiungere il repository Maven di Google al tuo progetto.
Apri il file settings.gradle
per il tuo progetto e aggiungi il repository google()
come mostrato di seguito:
Groovy
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } }
Kotlin
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } }
Aggiungi quanto segue alla fine del blocco 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" } }
Aggiungi quanto segue al file build.gradle
di ogni modulo per un'app:
Groovy
dependencies { // CameraX core library using the camera2 implementation def camerax_version = "1.5.0-beta01" // 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-beta01" // 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}") }
Per ulteriori informazioni sulla configurazione dell'app in modo che sia conforme a questi requisiti, consulta la sezione Dichiarazione delle dipendenze.
Interoperabilità di CameraX con Camera2
CameraX è basato su Camera2 ed espone modi per leggere e persino scrivere proprietà nell'implementazione di Camera2. Per tutti i dettagli, consulta il pacchetto di interoperabilità.
Per ulteriori informazioni su come CameraX ha configurato le proprietà di Camera2, utilizza
Camera2CameraInfo
per leggere CameraCharacteristics
sottostante. Puoi anche scegliere di scrivere le proprietà Camera2
sottostanti in uno dei due percorsi seguenti:
Utilizza
Camera2CameraControl
, che ti consente di impostare proprietà sull'CaptureRequest
sottostante, ad esempio la modalità di messa a fuoco automatica.Estendi un
UseCase
CameraX con unCamera2Interop.Extender
. In questo modo, puoi impostare le proprietà in CaptureRequest, proprio comeCamera2CameraControl
. Ti offre anche alcuni controlli aggiuntivi, come l'impostazione dello scenario di utilizzo dello stream per ottimizzare la videocamera per il tuo scenario di utilizzo. Per informazioni, consulta Utilizzare i casi d'uso di Stream per prestazioni migliori.
Il seguente esempio di codice utilizza i casi d'uso dello stream per l'ottimizzazione per una videochiamata.
Utilizza Camera2CameraInfo
per recuperare informazioni sull'eventuale disponibilità del caso d'uso dello stream di videochiamata. Poi, usa un
Camera2Interop.Extender
per impostare il caso d'uso dello stream sottostante.
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)
Risorse aggiuntive
Per saperne di più su CameraX, consulta le seguenti risorse aggiuntive.
Codelab
Esempio di codice