The second Android 11 Developer Preview is now available, test it out and share your feedback.

CameraX architecture

CameraX is an addition to Jetpack that makes it easier to leverage the capabilities of Camera2 APIs. This topic covers the architecture of CameraX, including its structure, how to work with the API, how to work with lifecycles, and how to combine use cases.

CameraX structure

Developers use CameraX to interface with a device’s camera through an abstraction called a use case. The following use cases are currently available:

  • Preview: accepts a surface for displaying a preview, such as a PreviewView.
  • Image analysis: provides CPU-accessible buffers for analysis, such as for machine learning.
  • Image capture: captures and saves a photo.

Use cases can be combined and active concurrently. For example, an app can let the user view the image that the camera sees using a preview use case, have an image analysis use case that determines whether the people in the photo are smiling, and include an image capture use case to take a picture once they are.

API model

To work with the library, you specify the following things:

  • The desired use case with configuration options.
  • What to do with output data by attaching listeners.
  • The intended flow, such as when to enable cameras and when to produce data, by binding the use case to Android Architecture Lifecycles.

You configure use cases using set() methods and finalize them with the build() method. Each use case object provides a set of use case-specific APIs. For example, the image capture use case provides a takePicture() method call.

Instead of an application placing specific start and stop method calls in onResume() and onPause(), the application specifies a lifecycle to associate the camera with, using cameraProvider.bindToLifecycle(). That lifecycle then informs CameraX when to configure the camera capture session and ensures camera state changes appropriately to match lifecycle transitions.

For implementation steps for each use case, see Implement a preview, Analyze images, and Image capture.

API model example

The preview use case interacts with a Surface for display. Applications create the use case with configuration options using the following code:

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.createSurfaceProvider(camera.cameraInfo))

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.createSurfaceProvider(camera.getCameraInfo());

For more example code, see the official CameraX sample app.

CameraX Lifecycles

CameraX observes a lifecycle to determine when to open the camera, when to create a capture session, and when to stop and shut down. Use case APIs provide method calls and callbacks to monitor progress.

As explained in Combine use cases, you can bind some mixes of use cases to a single lifecycle. When your app needs to support use cases that cannot be combined, you can do one of the following:

  • Group compatible use cases together into more than one fragment and then switch between fragments
  • Create a custom lifecycle component and use it to manually control the camera lifecycle

If you decouple your view and camera use cases' Lifecycle owners (for example, if you use a custom lifecycle or a retain fragment), then you must ensure that all use cases are unbound from CameraX by using CameraX.unbindAll() or by unbinding each use case individually. Alternatively, when you bind use cases to a Lifecycle, you can let CameraX manage opening and closing the capture session and unbinding the use cases.

If all of your camera functionality corresponds to the lifecycle of a single lifecycle-aware component such as an AppCompatActivity or an AppCompat fragment, then using the lifecycle of that component when binding all the desired use cases will ensure that the camera functionality is ready when the lifecycle-aware component is active, and safely disposed of, not consuming any resources, otherwise.

Custom LifecycleOwners

For advanced cases, you can create a custom LifecycleOwner to enable your app to explicitly control the CameraX session lifecycle instead of tying it to a standard Android LifecycleOwner.

The following code sample shows how to create a simple custom LifecycleOwner:

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

Using this LifecycleOwner, your app can place state transitions at desired points in its code. For more on implementing this functionality in your app, see Implementing a custom LifecycleOwner.

Concurrent use cases

Use cases can run concurrently. While use cases can be sequentially bound to a lifecycle, it is better to bind all use cases with a single call to CameraProcessProvider.bindToLifecycle(). For more information on best practices for configuration changes, see Handle configuration changes.

In the following code sample, the app specifies the two use cases to be created and run simultaneously. It also specifies the lifecycle to use for both use cases, so that they both start and stop according to the lifecycle.

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.createSurfaceProvider(camera.cameraInfo))
    }, 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 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.createSurfaceProvider(camera.getCameraInfo()));
        } catch (InterruptedException | ExecutionException e) {
            // Currently no exceptions thrown. cameraProviderFuture.get() should
            // not block since the listener is being called, so no need to
            // handle InterruptedException.
        }
    }, ContextCompat.getMainExecutor(this));
}

The following configuration combinations are supported:

Preview Analysis Image capture Combining use cases
Provide user a preview, take a photo, and analyze the image stream.
  Take a photo and analyze the image stream.
  Provide a preview with visual effects applied based on analysis of the images being streamed.
  Show what the camera sees and take a photo on user action.

When extensions are enabled, only ImageCapture and Preview are guaranteed. Depending on the OEM implementation, it may not be possible to also use ImageAnalysis.

ImageCapture doesn't work on its own, though Preview and ImageAnalysis do.

Permissions

Your app will need the CAMERA permission. To save images to files, it will also require the WRITE_EXTERNAL_STORAGE permission, except on devices running Android 10 or later.

For more information about configuring permissions for your app, read Request App Permissions.

Requirements

CameraX has the following minimum version requirements:

  • Android API level 21
  • Android Architecture Components 1.1.1

For lifecycle-aware activities, use FragmentActivity or AppCompatActivity.

Declare dependencies

To add a dependency on CameraX, you must add the Google Maven repository to your project.

Open the build.gradle file for your project and add the google() repository as shown in the following:

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

Add the following to the end of the Android block:

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

Add the following to each module’s build.gradle file for an app:

dependencies {
  // CameraX core library using the camera2 implementation
  def camerax_version = "1.0.0-beta02"
  implementation "androidx.camera:camera-camera2:${camerax_version}"
  // If you want to use the CameraX View class
  implementation "androidx.camera:camera-view:1.0.0-alpha09"
  // If you want to use the CameraX Extensions library
  implementation "androidx.camera:camera-extensions:1.0.0-alpha09"
  // If you want to use the CameraX Lifecycle library
  implementation "androidx.camera:camera-lifecycle:${camerax_version}"
}

For more information on configuring your app to conform to these requirements, see Declaring dependencies.

Additional resources

To learn more about CameraX, consult the following additional resources.

Codelab

  • Getting Started with CameraX
  • Code sample

  • Official CameraX sample app