ProcessCameraProvider  |  API reference  |  Android Developers

A singleton which can be used to bind the lifecycle of cameras to any LifecycleOwner within an application's process.

Only a single process camera provider can exist within a process, and it can be retrieved with getInstance.

Heavyweight resources, such as open and running camera devices, will be scoped to the lifecycle provided to bindToLifecycle. Other lightweight resources, such as static camera characteristics, may be retrieved and cached upon first retrieval of this provider with getInstance, and will persist for the lifetime of the process.

This is the standard provider for applications to use.

Public methods

addCameraPresenceListener

public void addCameraPresenceListener(
    @NonNull Executor executor,
    @NonNull CameraPresenceListener listener
)

Adds a listener for changes in camera presence.

The listener will be notified when cameras are added to or removed from the set of devices that can be used by CameraX. This list of "usable" cameras has already been processed by any configured CameraSelector limiters and compatibility filters.

Important Note on Synchronization: To prevent race conditions, this method immediately invokes CameraPresenceListener.onCamerasAdded once on the provided executor with a Set containing all cameras that are currently available. This guarantees that the listener's state is synchronized with the provider's state at the moment of registration.

This listener reports on persistent hardware changes and does not fire for temporary, recoverable errors, such as when a camera is in use by another application.

bindToLifecycle

@MainThread
public final @NonNull ConcurrentCamera bindToLifecycle(
    @NonNull List<ConcurrentCamera.SingleCameraConfig> singleCameraConfigs
)

Binds list of SingleCameraConfigs to LifecycleOwner.

The concurrent camera is only supporting two cameras currently. If the input list of SingleCameraConfigs have less or more than two SingleCameraConfigs, IllegalArgumentException will be thrown. If cameras are already used by other UseCases, UnsupportedOperationException will be thrown.

A logical camera is a grouping of two or more of those physical cameras. See Multi-camera API

If we want to open concurrent logical cameras, which are one front camera and one back camera, the device needs to support PackageManager.FEATURE_CAMERA_CONCURRENT. To set up concurrent logical camera, call availableConcurrentCameraInfos to get the list of available combinations of concurrent cameras. Each sub-list contains the CameraInfos for a combination of cameras that can be operated concurrently. Each logical camera can have its own UseCases and LifecycleOwner. See {@docRoot}training/camerax/architecture#lifecycles

There are two modes:

  1. Non-Composition mode: These two SingleCameraConfigs have different preview and video capture use cases and there is no CompositionSettings. In this mode, the two preview and the two video capture can stream separately. CameraX doesn't perform any composition. You can also bind an extra image capture along with the preview and the video capture use cases.

  2. Composition mode: If the concurrent logical cameras are binding the same instances of preview and video use cases, the concurrent cameras video recording is supported. The concurrent camera preview stream will be shared with video capture and record the concurrent cameras streams as a composited stream. The CompositionSettings can be used to configure the position of each camera stream and different layouts can be built. See CompositionSettings for more details. The composition mode only supports preview and video capture. ImageCapture is not supported. androidx.camera.core.CameraEffect can be applied on the composited stream. However, the mirrorMode of VideoCapture will be ignored. This means the recorded video will have the same mirrorMode as the preview.

If we want to open concurrent physical cameras, which are two front cameras or two back cameras, the device needs to support physical cameras and the capability could be checked via CameraInfo.isLogicalMultiCameraSupported. Each physical cameras can have its own UseCases but needs to have the same LifecycleOwner, otherwise IllegalArgumentException will be thrown.

If we want to open one physical camera, for example ultra wide, we just need to set physical camera id in CameraSelector and bind to lifecycle. All CameraX features will work normally when only a single physical camera is used.

If we want to open multiple physical cameras, we need to have multiple CameraSelectors, each in one SingleCameraConfig and set physical camera id, then bind to lifecycle with the SingleCameraConfigs. Internally each physical camera id will be set on UseCase, for example, Preview and call android.hardware.camera2.params.OutputConfiguration.setPhysicalCameraId.

Currently only two physical cameras for the same logical camera id are allowed and the device needs to support physical cameras by checking CameraInfo.isLogicalMultiCameraSupported. In addition, there is no guarantee or API to query whether the device supports multiple physical camera opening or not. Internally the library checks android.hardware.camera2.CameraDevice.isSessionConfigurationSupported, if the device does not support the multiple physical camera configuration, IllegalArgumentException will be thrown.

bindToLifecycle

public final @NonNull Camera bindToLifecycle(
    @NonNull LifecycleOwner lifecycleOwner,
    @NonNull CameraSelector cameraSelector,
    @NonNull SessionConfig sessionConfig
)

Binds a SessionConfig to a LifecycleOwner.

A SessionConfig encapsulates the configuration required for a camera session. This includes:

The state of the lifecycle will determine when the cameras are open, started, stopped and closed. When started, the use cases contained in the given SessionConfig receive camera data and the parameters of SessionConfig are used for configuring the camera including common field of view, effects and the session parameters.

Binding to a lifecycleOwner in state currently in Lifecycle.State.STARTED or greater will also initialize and start data capture. If the camera was already running this may cause a new initialization to occur temporarily stopping data from the camera before restarting it.

Updates the SessionConfig for a given LifecycleOwner by invoking bindToLifecycle again with the new SessionConfig. There is no need to call unbind or unbindAll; the previous SessionConfig and its associated UseCases will be implicitly unbound. This behavior also applies when rebinding to the same LifecycleOwner with a different CameraSelector, such as when switching the camera's lens facing.

Important Restrictions:

Violating these restrictions will result in an IllegalStateException.

The Camera returned is determined by the given camera selector, plus other internal requirements, possibly from use case configurations. The camera returned from bindToLifecycle may differ from the camera determined solely by a camera selector. If the camera selector can't resolve a valid camera under the requirements, an IllegalArgumentException will be thrown.

The following code example shows various aspects of binding a session config.

import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.Preview
import androidx.camera.core.SessionConfig

val preview =
    Preview.Builder().build().also { it.surfaceProvider = previewView.surfaceProvider }
val imageCapture = ImageCapture.Builder().build()
val sessionConfig =
    SessionConfig(
        useCases = listOf(preview, imageCapture),
        viewPort = previewView.getViewPort(preview.getTargetRotation()),
        effects = listOf(effect1),
    )
// Starts the camera with the given effect and viewPort when the lifecycleOwner is started.
cameraProvider.bindToLifecycle(
    lifecycleOwner,
    CameraSelector.DEFAULT_BACK_CAMERA,
    sessionConfig,
)

// To apply a different effect, unbind the previous SessionConfig and bind the new SessionConfig
// with the new effect.
val sessionConfigNewEffect =
    SessionConfig(
        useCases = listOf(preview, imageCapture),
        viewPort = previewView.getViewPort(preview.getTargetRotation()),
        effects = listOf(effect2),
    )
// Make sures to unbind the previous sessionConfig before binding the new one
cameraProvider.unbind(sessionConfig)
cameraProvider.bindToLifecycle(
    lifecycleOwner,
    CameraSelector.DEFAULT_BACK_CAMERA,
    sessionConfigNewEffect,
)

The following code snippet demonstrates binding a session config with feature groups.

import androidx.camera.core.CameraSelector
import androidx.camera.core.SessionConfig
import androidx.camera.core.featuregroup.GroupableFeature.Companion.FPS_60
import androidx.camera.core.featuregroup.GroupableFeature.Companion.HDR_HLG10
import androidx.camera.core.featuregroup.GroupableFeature.Companion.PREVIEW_STABILIZATION

// Starts the camera with feature groups configured.
cameraProvider.bindToLifecycle(
    lifecycleOwner,
    CameraSelector.DEFAULT_BACK_CAMERA,
    // HDR is mandatory in this camera configuration and an exception will be thrown if it's not
    // supported. 60 FPS and preview stabilization are optional and used if they are also
    // supported, with the 60 FPS having higher priority over preview stabilization.
    SessionConfig(
            useCases = useCases,
            requiredFeatureGroup = setOf(HDR_HLG10),
            preferredFeatureGroup = listOf(FPS_60, PREVIEW_STABILIZATION),
        )
        .apply {
            setFeatureSelectionListener { features ->
                Log.d(
                    "TAG",
                    "Features selected as per priority and device capabilities: $features",
                )

                // Update app UI based on the selected features if required
            }
        },
)

bindToLifecycle

@MainThread
public final @NonNull Camera bindToLifecycle(
    @NonNull LifecycleOwner lifecycleOwner,
    @NonNull CameraSelector cameraSelector,
    UseCase... useCases
)

Binds the collection of UseCase to a LifecycleOwner.

The state of the lifecycle will determine when the cameras are open, started, stopped and closed. When started, the use cases receive camera data.

Binding to a lifecycleOwner in state currently in Lifecycle.State.STARTED or greater will also initialize and start data capture. If the camera was already running this may cause a new initialization to occur temporarily stopping data from the camera before restarting it.

Multiple use cases can be bound via adding them all to a single bindToLifecycle call, or by using multiple bindToLifecycle calls. Using a single call that includes all the use cases helps to set up a camera session correctly for all uses cases, such as by allowing determination of resolutions depending on all the use cases bound being bound. If the use cases are bound separately, it will find the supported resolution with the priority depending on the binding sequence. If the use cases are bound with a single call, it will find the supported resolution with the priority in sequence of ImageCapture, Preview and then ImageAnalysis. The resolutions that can be supported depends on the camera device hardware level that there are some default guaranteed resolutions listed in android.hardware.camera2.CameraDevice.createCaptureSession.

Currently up to 3 use cases may be bound to a Lifecycle at any time. Exceeding capability of target camera device will throw an IllegalArgumentException.

A UseCase should only be bound to a single lifecycle and camera selector a time. Attempting to bind a use case to a lifecycle when it is already bound to another lifecycle is an error, and the use case binding will not change. Attempting to bind the same use case to multiple camera selectors is also an error and will not change the binding.

Binding different use cases to the same lifecycle with different camera selectors that resolve to distinct cameras is an error, resulting in an exception.

The Camera returned is determined by the given camera selector, plus other internal requirements, possibly from use case configurations. The camera returned from bindToLifecycle may differ from the camera determined solely by a camera selector. If the camera selector can't resolve a valid camera under the requirements, an IllegalArgumentException will be thrown.

Only UseCase bound to latest active Lifecycle can keep alive. UseCase bound to other Lifecycle will be stopped.

Parameters
@NonNull LifecycleOwner lifecycleOwner

The lifecycleOwner which controls the lifecycle transitions of the use cases.

@NonNull CameraSelector cameraSelector

The camera selector which determines the camera to use for set of use cases.

UseCase... useCases

The use cases to bind to a lifecycle.

Returns
@NonNull Camera

The Camera instance which is determined by the camera selector and internal requirements.

getAvailableConcurrentCameraInfos

public @NonNull List<@NonNull List<@NonNull CameraInfo>> getAvailableConcurrentCameraInfos()

Returns list of CameraInfo instances of the available concurrent cameras.

The available concurrent cameras include all combinations of cameras which could operate concurrently on the device. Each list maps to one combination of these camera's CameraInfo.

For example, to select a front camera and a back camera and bind to LifecycleOwner with preview UseCase, this function could be used with bindToLifecycle.

import androidx.camera.core.CameraSelector
import androidx.camera.core.ConcurrentCamera.SingleCameraConfig
import androidx.camera.core.Preview
import androidx.camera.core.UseCaseGroup
import androidx.camera.view.PreviewView

var cameraSelectorPrimary: CameraSelector? = null
var cameraSelectorSecondary: CameraSelector? = null
for (cameraInfoList in cameraProvider.availableConcurrentCameraInfos) {
    for (cameraInfo in cameraInfoList) {
        if (cameraInfo.lensFacing == CameraSelector.LENS_FACING_FRONT) {
            cameraSelectorPrimary = cameraInfo.getCameraSelector()
        } else if (cameraInfo.lensFacing == CameraSelector.LENS_FACING_BACK) {
            cameraSelectorSecondary = cameraInfo.getCameraSelector()
        }
    }
}
if (cameraSelectorPrimary == null || cameraSelectorSecondary == null) {
    return
}
val previewFront = Preview.Builder().build()
previewFront.surfaceProvider = frontPreviewView.getSurfaceProvider()
val primary =
    SingleCameraConfig(
        cameraSelectorPrimary,
        UseCaseGroup.Builder().addUseCase(previewFront).build(),
        lifecycleOwner,
    )
val previewBack = Preview.Builder().build()
previewBack.surfaceProvider = backPreviewView.getSurfaceProvider()
val secondary =
    SingleCameraConfig(
        cameraSelectorSecondary,
        UseCaseGroup.Builder().addUseCase(previewBack).build(),
        lifecycleOwner,
    )
cameraProvider.bindToLifecycle(listOf(primary, secondary))

getInstance

public static final @NonNull ListenableFuture<@NonNull ProcessCameraProvidergetInstance(@NonNull Context context)

Retrieves the ProcessCameraProvider associated with the current process.

The instance returned here can be used to bind use cases to any LifecycleOwner with bindToLifecycle.

The instance's configuration may be customized by subclassing the application's Application class and implementing CameraXConfig.Provider. For example, the sample implements CameraXConfig.Provider.getCameraXConfig and initializes this process camera provider with a Camera2 implementation from androidx.camera.camera2, and with a custom executor.

import androidx.camera.camera2.Camera2Config
import androidx.camera.core.CameraXConfig

@Override
fun getCameraXConfig(): CameraXConfig {
    return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
        .setCameraExecutor(executor)
        .setSchedulerHandler(handler)
        .build()
}

If it isn't possible to subclass the Application class, such as in library code, then the singleton can be configured via configureInstance before the first invocation of getInstance(context), the sample implements a customized camera provider that configures the instance before getting it.

import androidx.camera.camera2.Camera2Config
import androidx.camera.core.CameraProvider
import androidx.camera.core.CameraXConfig
import androidx.camera.lifecycle.ExperimentalCameraProviderConfiguration
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.lifecycle.ProcessCameraProvider.Companion.configureInstance
import androidx.concurrent.futures.await

var configured = false // Whether the camera provider has been configured or not.

@androidx.annotation.OptIn(ExperimentalCameraProviderConfiguration::class)
suspend fun getInstance(context: Context): ProcessCameraProvider {
    synchronized(CameraProvider::class.java) {
        if (!configured) {
            configured = true
            configureInstance(
                CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
                    .setCameraExecutor(executor)
                    .setSchedulerHandler(scheduleHandler)
                    .build()
            )
        }
    }
    return ProcessCameraProvider.getInstance(context).await()
}

If no CameraXConfig.Provider is implemented by Application, or if the singleton has not been configured via configureInstance a default configuration will be used.

hasCamera

public boolean hasCamera(@NonNull CameraSelector cameraSelector)

Checks whether this provider supports at least one camera that meets the requirements from a CameraSelector.

If this method returns true, then the camera selector can be used to bind use cases and retrieve a Camera instance.

Returns
boolean

true if the device has at least one available camera, otherwise false.

removeCameraPresenceListener

public void removeCameraPresenceListener(@NonNull CameraPresenceListener listener)

Removes a previously registered camera presence listener.

Once removed, the listener will no longer receive updates. If the listener was not previously registered, this method is a no-op.

unbind

public final void unbind(@NonNull SessionConfig sessionConfig)

Unbinds the specified SessionConfig instance from the lifecycle provider.

This method will only unbind the session if the provided sessionConfig is the exact same instance that was previously used for binding.

This SessionConfig contains the UseCases to be detached from the camera. This will initiate a close of every open camera which has zero UseCase associated with it at the end of this call.

After unbinding the SessionConfig, another SessionConfig can be bound again and its UseCases can be bound to another Lifecycle.

Parameters
@NonNull SessionConfig sessionConfig

The sessionConfig that contains the collection of use cases to remove.

unbind

@MainThread
public final void unbind(UseCase... useCases)

Unbinds all specified use cases from the lifecycle provider.

This will initiate a close of every open camera which has zero UseCase associated with it at the end of this call.

If a use case in the argument list is not bound, then it is simply ignored.

After unbinding a UseCase, the UseCase can be bound to another Lifecycle however listeners and settings should be reset by the application.

Parameters
UseCase... useCases

The collection of use cases to remove.

unbindAll

@MainThread
public final void unbindAll()

Unbinds all use cases from the lifecycle provider and removes them from CameraX.

This will initiate a close of every currently open camera.