CameraX: Learn how to use CameraController

Husayn Hakeem
Android Developers
Published in
5 min readDec 15, 2020

CameraX currently provides CameraView, a View that displays a preview of the camera, while also providing methods to take pictures, control the camera, and query camera information. CameraView clearly takes on more responsibilities than a View should, as it participates in the View hierarchy and displays content, while also owning camera resources that exist outside the scope and lifecycle of the View hierarchy.

CameraView violates the separation of concerns principle, making it harder to guarantee its robustness in all corner cases. As a result, it will be marked as deprecated and removed before CameraX’s view artifact reaches Beta. It has been split up into two parts that you should use instead: PreviewView which handles its view-related tasks, and CameraController which handles the camera operations.

PreviewView has been available in CameraX’s view artifact for a while. CameraController, however, was introduced recently in version alpha19. It’s a high-level, all-in-one API that provides a way to easily access and manipulate core camera features, including displaying a camera preview, taking a picture, and analyzing camera frames. It does so while matching the output of its use cases to the viewfinder’s preview, thus providing a “What You See Is What You Get” (WYSIWYG) experience, a highly requested feature by developers that makes CameraX’s usage very intuitive. It also takes care of initializing the camera and adapting its output to device rotation.

To offload the burden of starting, stopping, and closing the camera, CameraX also introduced LifecycleCameraController, which offers all the convenience of CameraController with the added benefit of binding the camera’s lifecycle to that of a LifecycleOwner, typically the lifecycle of a Fragment or Activity. This allows the lifecycle’s state to control when the camera is opened, stopped, and closed.

Using PreviewView with CameraController is fairly simple, and can be done as follows:

The following sections list the mappings from CameraView to CameraController. You can use them to migrate from CameraView to PreviewView and CameraController.

Camera Initialization

Unlike CameraView, CameraController provides a ListenableFuture to monitor its initialization. It’s an asynchronous operation during which CameraController initializes CameraX and binds its use cases. The benefit of using this ListenableFuture is twofold:

  • Once it successfully completes, you can allow your users to start interacting with the camera, for example by taking pictures and zooming in and out of the viewfinder.
  • In case it fails, you can gracefully handle the error and communicate it to your users.

In case you aren’t familiar with ListenableFuture: it wraps an asynchronous operation and allows you to attach a listener that’s invoked on its completion. In case the operation has already finished, the future returns immediately.

Camera Lifecycle

Similar to CameraView, LifecycleCameraController ties control of the camera to a lifecycle. You must bind a valid LifecycleOwner to the controller for it to be operational.

LifecycleCameraController provides an additional unbind() method which allows you to prematurely close the camera before the lifecycle that it’s bound to comes to an end. This is useful in cases in which the camera isn’t integral to a screen and isn’t needed for its entire lifecycle. Alternatively, you can define your custom LifecycleOwner to control when to close the camera, but unbind() can be more convenient in some cases.

Camera Control

- Zoom

CameraController allows you to observe the camera’s zoom state via a LiveData instance. This state becomes available once the camera is open, and holds both static information, like the maximum and minimum zoom ratios, and dynamic information, like the current zoom ratio. You can verify whether zoom is supported using the maximum zoom ratio as follows:

CameraController additionally provides methods to get and set linear zoom, which can vary between 0 (minimum zoom) and 1 (maximum zoom).

Finally, like CameraView, CameraController allows you to enable or disable pinch-to-zoom. When enabled, CameraController handles pinch gestures on its attached PreviewView to zoom the camera in and out.

- Flash/Torch

CameraController provides similar methods as CameraView to set and query the image capture’s flash mode, but unlike CameraView, it allows observing changes in the camera’s torch state via a LiveData instance, which emits TorchState.OFF and TorchState.ON.

- Focus/Metering

While CameraView automatically acquires focus on any region of the viewfinder that is tapped, CameraController provides more flexibility by allowing you to enable or disable tap-to-focus functionality. When enabled, CameraController handles touch events on its attached PreviewView by focusing the camera on tapped regions.

Camera Selection

CameraController provides you with more control over which camera to use by allowing you to specify a CameraSelector to select the camera it uses. This allows you to rotate between various cameras when toggling cameras, instead of only the default front and back cameras, which was a limitation of CameraView.toggleCamera(). You can still implement this fairly easily, though, using CameraController as follows.

Camera Use Cases

CameraController supports all of CameraX’s use cases: Preview, ImageAnalysis, and ImageCapture, thus making it a more robust camera solution compared to CameraView. It also matches the output of ImageAnalysis and ImageCapture to the preview’s display, thereby providing a WYSIWYG experience.

CameraController’s default state enables preview, image analysis, and image capture. The Preview use case is always enabled, so you can choose the remaining use cases to enable or disable depending on your camera usage needs.

- Preview

Unlike CameraView which wraps (and thus hides) a PreviewView instance and forwards method calls to it, CameraController is decoupled from PreviewView. This means you have more control over the viewfinder, and can access and manipulate it directly.

- ImageAnalysis

Unlike CameraView, CameraController supports the ImageAnalysis use case. You can set and clear its Analyzer, while also configuring its image-processing pipeline.

- ImageCapture

CameraController provides the same methods as CameraView to take a picture, specify the image save destination, and provide the image capture callbacks.

One thing to note is that CameraController mirrors an image captured with a front-facing camera, unless you disable this in the OutputFileOptions’s metadata by calling Metadata.setReversedHorizontal(false).

Conclusion

In summary:

  • Because CameraView is handling the responsibilities of both a view and a controller in the MVC sense, CameraX is deprecating it and splitting it into PreviewView and the newly introduced CameraController.
  • CameraController handles camera initialization, as well as the creation and configuration of its use cases.
  • CameraController provides a WYSIWYG experience by matching the output of its use cases to PreviewView’s display.
  • CameraController listens to the device’s motion sensor to correctly rotate the output of its use cases.
  • CameraController adds support for the ImageAnalysis use case, making it a more robust camera solution that provides easy access to all of CameraX’s use cases.
  • CameraController supports all of CameraView’s features and more, such as enabling and disabling tap-to-focus, getting and setting linear zoom, and observing dynamic camera information like the zoom and torch states.
  • LifecycleCameraController is a CameraController that binds the camera’s lifecycle to that of a LifecycleOwner, typically the lifecycle of the UI.

Want more CameraX goodness? Check out:

--

--