Архитектура CameraX

На этой странице рассказывается об архитектуре CameraX, включая ее структуру, работу с API, работу с жизненными циклами и объединение вариантов использования.

Структура CameraX

Вы можете использовать CameraX для взаимодействия с камерой устройства через абстракцию, называемую вариантом использования. Доступны следующие варианты использования:

  • Preview : принимает поверхность для отображения предварительного просмотра, например PreviewView .
  • Анализ изображений : предоставляет доступные ЦП буферы для анализа, например, для машинного обучения.
  • Захват изображения : захватывает и сохраняет фотографию.
  • Захват видео : захватывайте видео и аудио с помощью VideoCapture

Варианты использования можно комбинировать и активировать одновременно. Например, приложение может позволить пользователю просматривать изображение, которое видит камера, используя вариант использования предварительного просмотра, иметь вариант использования для анализа изображений, который определяет, улыбаются ли люди на фотографии, и включать вариант использования для захвата изображения, чтобы сделать снимок. раз они есть.

Модель API

Для работы с библиотекой вы указываете следующие вещи:

  • Желаемый вариант использования с опциями конфигурации.
  • Что делать с выходными данными, подключив прослушиватели.
  • Предполагаемый поток, например, когда включать камеры и когда создавать данные, путем привязки варианта использования к жизненным циклам архитектуры Android .

Есть два способа написать приложение CameraX: CameraController (отлично, если вам нужен самый простой способ использования CameraX) или CameraProvider (отлично, если вам нужна большая гибкость).

КамераКонтроллер

CameraController обеспечивает большую часть основных функций CameraX в одном классе. Он требует небольшого количества кода настройки и автоматически выполняет инициализацию камеры, управление вариантами использования, вращение цели, фокусировку касанием, масштабирование пальцами и многое другое. Конкретным классом, расширяющим CameraController является LifecycleCameraController .

Котлин

val previewView: PreviewView = viewBinding.previewView
var cameraController = LifecycleCameraController(baseContext)
cameraController.bindToLifecycle(this)
cameraController.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
previewView.controller = cameraController

Ява

PreviewView previewView = viewBinding.previewView;
LifecycleCameraController cameraController = new LifecycleCameraController(baseContext);
cameraController.bindToLifecycle(this);
cameraController.setCameraSelector(CameraSelector.DEFAULT_BACK_CAMERA);
previewView.setController(cameraController);

UseCase по умолчанию для CameraControllerPreview , ImageCapture и ImageAnalysis . Чтобы отключить ImageCapture или ImageAnalysis или включить VideoCapture , используйте метод setEnabledUseCases() .

Дополнительные сведения об использовании CameraController см. в примере сканера QR-кода или в видеоролике об основах CameraController .

Поставщик камеры

CameraProvider по-прежнему прост в использовании, но поскольку большую часть настройки берет на себя разработчик приложения, появляется больше возможностей для настройки конфигурации, например включение вращения выходного изображения или установка формата выходного изображения в ImageAnalysis . Вы также можете использовать специальную Surface для предварительного просмотра камеры, что обеспечивает большую гибкость, тогда как с CameraController вам необходимо использовать PreviewView . Использование существующего кода Surface может быть полезным, если он уже является входными данными для других частей вашего приложения.

Вы настраиваете варианты использования с помощью методов set() и завершаете их с помощью метода build() . Каждый объект варианта использования предоставляет набор API-интерфейсов для конкретного варианта использования. Например, вариант использования захвата изображений предусматривает вызов метода takePicture() .

Вместо того, чтобы приложение помещало определенные вызовы методов запуска и остановки в onResume() и onPause() , приложение определяет жизненный цикл, с которым нужно связать камеру, используя cameraProvider.bindToLifecycle() . Затем этот жизненный цикл сообщает CameraX, когда следует настроить сеанс захвата камеры, и обеспечивает соответствующие изменения состояния камеры в соответствии с переходами жизненного цикла.

Шаги реализации для каждого варианта использования см. в разделах «Реализация предварительного просмотра» , «Анализ изображений» , «Захват изображений» и «Захват видео».

Вариант использования предварительного просмотра взаимодействует с Surface для отображения. Приложения создают вариант использования с параметрами конфигурации, используя следующий код:

Котлин

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())

Ява

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();

Дополнительные примеры кода см. в официальном примере приложения CameraX .

Жизненный цикл CameraX

CameraX наблюдает за жизненным циклом, чтобы определить, когда открывать камеру, когда создавать сеанс захвата, а когда останавливаться и выключаться. API вариантов использования предоставляют вызовы методов и обратные вызовы для отслеживания прогресса.

Как поясняется в разделе «Объединение вариантов использования» , вы можете связать некоторые сочетания вариантов использования с одним жизненным циклом. Если вашему приложению необходимо поддерживать варианты использования, которые невозможно объединить, вы можете выполнить одно из следующих действий:

  • Группируйте совместимые варианты использования в несколько фрагментов , а затем переключайтесь между фрагментами.
  • Создайте собственный компонент жизненного цикла и используйте его для ручного управления жизненным циклом камеры.

Если вы отделяете владельцев жизненного цикла вариантов использования представления и камеры (например, если вы используете пользовательский жизненный цикл или фрагмент сохранения ), то вы должны убедиться, что все варианты использования отвязаны от CameraX с помощью ProcessCameraProvider.unbindAll() или путем отмены привязки. каждый вариант использования индивидуально. Альтернативно, когда вы привязываете варианты использования к жизненному циклу, вы можете позволить CameraX управлять открытием и закрытием сеанса захвата, а также отменой привязки вариантов использования.

Если все функции вашей камеры соответствуют жизненному циклу одного компонента, учитывающего жизненный цикл, такого как AppCompatActivity или фрагмент AppCompat , то использование жизненного цикла этого компонента при привязке всех желаемых вариантов использования гарантирует, что функциональность камеры будет готова, когда Компонент с учетом жизненного цикла активен и безопасно удаляется, в противном случае не потребляя никаких ресурсов.

Пользовательские владельцы жизненного цикла

В сложных случаях вы можете создать собственный LifecycleOwner , чтобы ваше приложение могло явно управлять жизненным циклом сеанса CameraX вместо привязки его к стандартному LifecycleOwner для Android.

В следующем примере кода показано, как создать простой пользовательский LifecycleOwner:

Котлин

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

Ява

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

Используя этот LifecycleOwner , ваше приложение может размещать переходы между состояниями в нужных точках своего кода. Дополнительные сведения о реализации этой функции в вашем приложении см. в разделе «Реализация пользовательского LifecycleOwner» .

Параллельные варианты использования

Варианты использования могут выполняться одновременно. Хотя варианты использования могут быть последовательно привязаны к жизненному циклу, лучше связать все варианты использования одним вызовом CameraProcessProvider.bindToLifecycle() . Дополнительные сведения о рекомендациях по изменению конфигурации см. в разделе Обработка изменений конфигурации .

В следующем примере кода приложение указывает два варианта использования, которые необходимо создать и запустить одновременно. Он также определяет жизненный цикл, который будет использоваться для обоих вариантов использования, чтобы они запускались и останавливались в соответствии с жизненным циклом.

Котлин

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

Ява

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

Гарантированно поддерживаются следующие комбинации конфигураций (когда требуется предварительный просмотр или захват видео, но не оба одновременно):

Предварительный просмотр или видеозахват Захват изображения Анализ Описания
Предоставьте пользователю предварительный просмотр или запись видео, сделайте снимок и проанализируйте поток изображений.
Сделайте снимок и проанализируйте поток изображений.
Предоставьте пользователю предварительный просмотр или запись видео и сделайте снимок.
Предоставьте пользователю предварительный просмотр или запись видео, а также анализируйте поток изображений.

Если требуются и предварительный просмотр, и захват видео, условно поддерживаются следующие комбинации вариантов использования:

Предварительный просмотр Захват видео Захват изображения Анализ Особое требование
Гарантия на все камеры.
LIMITED (или лучшее) устройство камеры.
Устройство камеры УРОВНЯ_3 (или выше).

Кроме того,

  • Каждый вариант использования может работать сам по себе. Например, приложение может записывать видео без предварительного просмотра.
  • Если расширения включены, гарантированно будет работать только комбинация ImageCapture и Preview . В зависимости от реализации OEM может оказаться невозможным добавить ImageAnalysis ; расширения не могут быть включены для варианта использования VideoCapture . Дополнительную информацию см. в справочном документе по расширению .
  • В зависимости от возможностей камеры некоторые камеры могут поддерживать эту комбинацию в режимах с более низким разрешением, но не могут поддерживать ту же комбинацию в некоторых более высоких разрешениях.

Поддерживаемый уровень оборудования можно получить из Camera2CameraInfo . Например, следующий код проверяет, является ли задняя камера по умолчанию устройством LEVEL_3 :

Котлин

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

Ява

@androidx.annotation.OptIn(markerClass = ExperimentalCamera2Interop.class)
Boolean isBackCameraLevel3Device(ProcessCameraProvider cameraProvider) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        List\ 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;
}

Разрешения

Вашему приложению потребуется разрешение CAMERA . Для сохранения изображений в файлы также потребуется разрешение WRITE_EXTERNAL_STORAGE , за исключением устройств под управлением Android 10 или более поздней версии.

Дополнительные сведения о настройке разрешений для вашего приложения см. в разделе Запрос разрешений приложения .

Требования

CameraX имеет следующие минимальные требования к версии:

  • Android API уровня 21
  • Компоненты архитектуры Android 1.1.1

Для действий с учетом жизненного цикла используйте FragmentActivity или AppCompatActivity .

Объявить зависимости

Чтобы добавить зависимость от CameraX, вам необходимо добавить в свой проект репозиторий Google Maven .

Откройте файл settings.gradle для своего проекта и добавьте репозиторий google() как показано ниже:

классный

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

Котлин

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

Добавьте следующее в конец блока Android:

классный

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    // For Kotlin projects
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

Котлин

android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    // For Kotlin projects
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

Добавьте следующее в файл build.gradle каждого модуля для приложения:

классный

dependencies {
  // CameraX core library using the camera2 implementation
  def camerax_version = "1.5.0-alpha01"
  // 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}"
}

Котлин

dependencies {
    // CameraX core library using the camera2 implementation
    val camerax_version = "1.5.0-alpha01"
    // 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}")
}

Дополнительные сведения о настройке приложения в соответствии с этими требованиями см. в разделе Объявление зависимостей .

Совместимость CameraX с Camera2

CameraX построен на базе Camera2, а CameraX предоставляет способы чтения и даже записи свойств в реализации Camera2. Полную информацию см. в пакете Interop .

Для получения дополнительных сведений о том, как CameraX настроила свойства Camera2, используйте Camera2CameraInfo для чтения базового CameraCharacteristics . Вы также можете записать базовые свойства Camera2 одним из следующих двух способов:

В следующем примере кода используются варианты использования потока для оптимизации видеовызова. Используйте Camera2CameraInfo чтобы узнать, доступен ли вариант использования потока видеовызовов. Затем используйте Camera2Interop.Extender , чтобы установить вариант использования базового потока.

Котлин

// 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)

Ява

// 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)

Дополнительные ресурсы

Чтобы узнать больше о CameraX, обратитесь к следующим дополнительным ресурсам.

Кодлаб

  • Начало работы с CameraX
  • Пример кода

  • Примеры приложений CameraX
  • ,

    На этой странице рассказывается об архитектуре CameraX, включая ее структуру, работу с API, работу с жизненными циклами и объединение вариантов использования.

    Структура CameraX

    Вы можете использовать CameraX для взаимодействия с камерой устройства через абстракцию, называемую вариантом использования. Доступны следующие варианты использования:

    • Preview : принимает поверхность для отображения предварительного просмотра, например PreviewView .
    • Анализ изображений : предоставляет доступные ЦП буферы для анализа, например, для машинного обучения.
    • Захват изображения : захватывает и сохраняет фотографию.
    • Захват видео : захватывайте видео и аудио с помощью VideoCapture

    Варианты использования можно комбинировать и активировать одновременно. Например, приложение может позволить пользователю просматривать изображение, которое видит камера, используя вариант использования предварительного просмотра, иметь вариант использования для анализа изображений, который определяет, улыбаются ли люди на фотографии, и включать вариант использования для захвата изображения, чтобы сделать снимок. раз они есть.

    Модель API

    Для работы с библиотекой вы указываете следующие вещи:

    • Желаемый вариант использования с опциями конфигурации.
    • Что делать с выходными данными, подключив прослушиватели.
    • Предполагаемый поток, например, когда включать камеры и когда создавать данные, путем привязки варианта использования к жизненным циклам архитектуры Android .

    Есть два способа написать приложение CameraX: CameraController (отлично, если вам нужен самый простой способ использования CameraX) или CameraProvider (отлично, если вам нужна большая гибкость).

    КамераКонтроллер

    CameraController обеспечивает большую часть основных функций CameraX в одном классе. Он требует небольшого количества кода настройки и автоматически выполняет инициализацию камеры, управление вариантами использования, вращение цели, фокусировку касанием, масштабирование пальцами и многое другое. Конкретным классом, расширяющим CameraController является LifecycleCameraController .

    Котлин

    val previewView: PreviewView = viewBinding.previewView
    var cameraController = LifecycleCameraController(baseContext)
    cameraController.bindToLifecycle(this)
    cameraController.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
    previewView.controller = cameraController
    

    Ява

    PreviewView previewView = viewBinding.previewView;
    LifecycleCameraController cameraController = new LifecycleCameraController(baseContext);
    cameraController.bindToLifecycle(this);
    cameraController.setCameraSelector(CameraSelector.DEFAULT_BACK_CAMERA);
    previewView.setController(cameraController);
    

    UseCase по умолчанию для CameraControllerPreview , ImageCapture и ImageAnalysis . Чтобы отключить ImageCapture или ImageAnalysis или включить VideoCapture , используйте метод setEnabledUseCases() .

    Дополнительные сведения об использовании CameraController см. в примере сканера QR-кода или в видеоролике об основах CameraController .

    Поставщик камеры

    CameraProvider по-прежнему прост в использовании, но поскольку большую часть настройки берет на себя разработчик приложения, появляется больше возможностей для настройки конфигурации, например включение вращения выходного изображения или установка формата выходного изображения в ImageAnalysis . Вы также можете использовать специальную Surface для предварительного просмотра камеры, что обеспечивает большую гибкость, тогда как с CameraController вам необходимо использовать PreviewView . Использование существующего кода Surface может быть полезным, если он уже является входными данными для других частей вашего приложения.

    Вы настраиваете варианты использования с помощью методов set() и завершаете их с помощью метода build() . Каждый объект варианта использования предоставляет набор API-интерфейсов для конкретного варианта использования. Например, вариант использования захвата изображений предусматривает вызов метода takePicture() .

    Вместо того, чтобы приложение помещало определенные вызовы методов запуска и остановки в onResume() и onPause() , приложение определяет жизненный цикл, с которым нужно связать камеру, используя cameraProvider.bindToLifecycle() . Затем этот жизненный цикл сообщает CameraX, когда следует настроить сеанс захвата камеры, и обеспечивает соответствующие изменения состояния камеры в соответствии с переходами жизненного цикла.

    Шаги реализации для каждого варианта использования см. в разделах «Реализация предварительного просмотра» , «Анализ изображений» , «Захват изображений» и «Захват видео».

    Вариант использования предварительного просмотра взаимодействует с Surface для отображения. Приложения создают вариант использования с параметрами конфигурации, используя следующий код:

    Котлин

    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())
    

    Ява

    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();
    

    Дополнительные примеры кода см. в официальном примере приложения CameraX .

    Жизненный цикл CameraX

    CameraX наблюдает за жизненным циклом, чтобы определить, когда открывать камеру, когда создавать сеанс захвата, а когда останавливаться и выключаться. API вариантов использования предоставляют вызовы методов и обратные вызовы для отслеживания прогресса.

    Как поясняется в разделе «Объединение вариантов использования» , вы можете связать некоторые сочетания вариантов использования с одним жизненным циклом. Если вашему приложению необходимо поддерживать варианты использования, которые невозможно объединить, вы можете выполнить одно из следующих действий:

    • Группируйте совместимые варианты использования в несколько фрагментов , а затем переключайтесь между фрагментами.
    • Создайте собственный компонент жизненного цикла и используйте его для ручного управления жизненным циклом камеры.

    Если вы отделяете владельцев жизненного цикла вариантов использования представления и камеры (например, если вы используете пользовательский жизненный цикл или фрагмент сохранения ), то вы должны убедиться, что все варианты использования отвязаны от CameraX с помощью ProcessCameraProvider.unbindAll() или путем отмены привязки. каждый вариант использования индивидуально. Альтернативно, когда вы привязываете варианты использования к жизненному циклу, вы можете позволить CameraX управлять открытием и закрытием сеанса захвата, а также отменой привязки вариантов использования.

    Если все функции вашей камеры соответствуют жизненному циклу одного компонента, учитывающего жизненный цикл, такого как AppCompatActivity или фрагмент AppCompat , то использование жизненного цикла этого компонента при привязке всех желаемых вариантов использования гарантирует, что функциональность камеры будет готова, когда Компонент с учетом жизненного цикла активен и безопасно удаляется, в противном случае не потребляя никаких ресурсов.

    Пользовательские владельцы жизненного цикла

    В сложных случаях вы можете создать собственный LifecycleOwner , чтобы ваше приложение могло явно управлять жизненным циклом сеанса CameraX вместо привязки его к стандартному LifecycleOwner для Android.

    В следующем примере кода показано, как создать простой пользовательский LifecycleOwner:

    Котлин

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

    Ява

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

    Используя этот LifecycleOwner , ваше приложение может размещать переходы между состояниями в нужных точках своего кода. Дополнительные сведения о реализации этой функции в вашем приложении см. в разделе «Реализация пользовательского LifecycleOwner» .

    Параллельные варианты использования

    Варианты использования могут выполняться одновременно. Хотя варианты использования могут быть последовательно привязаны к жизненному циклу, лучше связать все варианты использования одним вызовом CameraProcessProvider.bindToLifecycle() . Дополнительные сведения о рекомендациях по изменению конфигурации см. в разделе Обработка изменений конфигурации .

    В следующем примере кода приложение указывает два варианта использования, которые необходимо создать и запустить одновременно. Он также определяет жизненный цикл, который будет использоваться для обоих вариантов использования, чтобы они запускались и останавливались в соответствии с жизненным циклом.

    Котлин

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

    Ява

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

    Гарантированно поддерживаются следующие комбинации конфигураций (когда требуется предварительный просмотр или захват видео, но не оба одновременно):

    Предварительный просмотр или видеозахват Захват изображения Анализ Описания
    Предоставьте пользователю предварительный просмотр или запись видео, сделайте снимок и проанализируйте поток изображений.
    Сделайте снимок и проанализируйте поток изображений.
    Предоставьте пользователю предварительный просмотр или запись видео и сделайте снимок.
    Предоставьте пользователю предварительный просмотр или запись видео, а также анализируйте поток изображений.

    Если требуются и предварительный просмотр, и захват видео, условно поддерживаются следующие комбинации вариантов использования:

    Предварительный просмотр Захват видео Захват изображения Анализ Особое требование
    Гарантия на все камеры.
    LIMITED (или лучшее) устройство камеры.
    Устройство камеры УРОВНЯ_3 (или выше).

    Кроме того,

    • Каждый вариант использования может работать сам по себе. Например, приложение может записывать видео без предварительного просмотра.
    • Если расширения включены, гарантированно будет работать только комбинация ImageCapture и Preview . В зависимости от реализации OEM может оказаться невозможным добавить ImageAnalysis ; расширения не могут быть включены для варианта использования VideoCapture . Дополнительную информацию см. в справочном документе по расширению .
    • В зависимости от возможностей камеры некоторые камеры могут поддерживать эту комбинацию в режимах с более низким разрешением, но не могут поддерживать ту же комбинацию в некоторых более высоких разрешениях.

    Поддерживаемый уровень оборудования можно получить из Camera2CameraInfo . Например, следующий код проверяет, является ли задняя камера по умолчанию устройством LEVEL_3 :

    Котлин

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

    Ява

    @androidx.annotation.OptIn(markerClass = ExperimentalCamera2Interop.class)
    Boolean isBackCameraLevel3Device(ProcessCameraProvider cameraProvider) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            List\ 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;
    }
    

    Разрешения

    Вашему приложению потребуется разрешение CAMERA . Для сохранения изображений в файлы также потребуется разрешение WRITE_EXTERNAL_STORAGE , за исключением устройств под управлением Android 10 или более поздней версии.

    Дополнительные сведения о настройке разрешений для вашего приложения см. в разделе Запрос разрешений приложения .

    Требования

    CameraX имеет следующие минимальные требования к версии:

    • Android API уровня 21
    • Компоненты архитектуры Android 1.1.1

    Для действий с учетом жизненного цикла используйте FragmentActivity или AppCompatActivity .

    Объявить зависимости

    Чтобы добавить зависимость от CameraX, вам необходимо добавить в свой проект репозиторий Google Maven .

    Откройте файл settings.gradle для своего проекта и добавьте репозиторий google() как показано ниже:

    классный

    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            google()
            mavenCentral()
        }
    }
    

    Котлин

    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            google()
            mavenCentral()
        }
    }
    

    Добавьте следующее в конец блока Android:

    классный

    android {
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
        // For Kotlin projects
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
    

    Котлин

    android {
        compileOptions {
            sourceCompatibility = JavaVersion.VERSION_1_8
            targetCompatibility = JavaVersion.VERSION_1_8
        }
        // For Kotlin projects
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
    

    Добавьте следующее в файл build.gradle каждого модуля для приложения:

    классный

    dependencies {
      // CameraX core library using the camera2 implementation
      def camerax_version = "1.5.0-alpha01"
      // 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}"
    }

    Котлин

    dependencies {
        // CameraX core library using the camera2 implementation
        val camerax_version = "1.5.0-alpha01"
        // 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}")
    }

    Дополнительные сведения о настройке приложения в соответствии с этими требованиями см. в разделе Объявление зависимостей .

    Совместимость CameraX с Camera2

    CameraX построен на базе Camera2, а CameraX предоставляет способы чтения и даже записи свойств в реализации Camera2. Полную информацию см. в пакете Interop .

    Для получения дополнительных сведений о том, как CameraX настроила свойства Camera2, используйте Camera2CameraInfo для чтения базового CameraCharacteristics . Вы также можете записать базовые свойства Camera2 одним из следующих двух способов:

    В следующем примере кода используются варианты использования потока для оптимизации видеовызова. Используйте Camera2CameraInfo чтобы узнать, доступен ли вариант использования потока видеовызовов. Затем используйте Camera2Interop.Extender , чтобы установить вариант использования базового потока.

    Котлин

    // 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)
    

    Ява

    // 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)
    

    Дополнительные ресурсы

    Чтобы узнать больше о CameraX, обратитесь к следующим дополнительным ресурсам.

    Кодлаб

  • Начало работы с CameraX
  • Пример кода

  • Примеры приложений CameraX
  • ,

    На этой странице рассказывается об архитектуре CameraX, включая ее структуру, работу с API, работу с жизненными циклами и объединение вариантов использования.

    Структура CameraX

    Вы можете использовать CameraX для взаимодействия с камерой устройства через абстракцию, называемую вариантом использования. Доступны следующие варианты использования:

    • Preview : принимает поверхность для отображения предварительного просмотра, например PreviewView .
    • Анализ изображений : предоставляет доступные ЦП буферы для анализа, например, для машинного обучения.
    • Захват изображения : захватывает и сохраняет фотографию.
    • Захват видео : захватывайте видео и аудио с помощью VideoCapture

    Варианты использования можно комбинировать и активировать одновременно. Например, приложение может позволить пользователю просматривать изображение, которое видит камера, используя вариант использования предварительного просмотра, иметь вариант использования для анализа изображений, который определяет, улыбаются ли люди на фотографии, и включать вариант использования для захвата изображения, чтобы сделать снимок. раз они есть.

    Модель API

    Для работы с библиотекой вы указываете следующие вещи:

    • Желаемый вариант использования с опциями конфигурации.
    • Что делать с выходными данными, подключив прослушиватели.
    • Предполагаемый поток, например, когда включать камеры и когда создавать данные, путем привязки варианта использования к жизненным циклам архитектуры Android .

    Есть два способа написать приложение CameraX: CameraController (отлично, если вам нужен самый простой способ использования CameraX) или CameraProvider (отлично, если вам нужна большая гибкость).

    КамераКонтроллер

    CameraController обеспечивает большую часть основных функций CameraX в одном классе. Он требует небольшого количества кода настройки и автоматически выполняет инициализацию камеры, управление вариантами использования, вращение цели, фокусировку касанием, масштабирование пальцами и многое другое. Конкретным классом, расширяющим CameraController является LifecycleCameraController .

    Котлин

    val previewView: PreviewView = viewBinding.previewView
    var cameraController = LifecycleCameraController(baseContext)
    cameraController.bindToLifecycle(this)
    cameraController.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
    previewView.controller = cameraController
    

    Ява

    PreviewView previewView = viewBinding.previewView;
    LifecycleCameraController cameraController = new LifecycleCameraController(baseContext);
    cameraController.bindToLifecycle(this);
    cameraController.setCameraSelector(CameraSelector.DEFAULT_BACK_CAMERA);
    previewView.setController(cameraController);
    

    UseCase по умолчанию для CameraControllerPreview , ImageCapture и ImageAnalysis . Чтобы отключить ImageCapture или ImageAnalysis или включить VideoCapture , используйте метод setEnabledUseCases() .

    Дополнительные сведения об использовании CameraController см. в примере сканера QR-кода или в видеоролике об основах CameraController .

    Поставщик камеры

    CameraProvider по-прежнему прост в использовании, но поскольку большую часть настройки берет на себя разработчик приложения, появляется больше возможностей для настройки конфигурации, например включение вращения выходного изображения или установка формата выходного изображения в ImageAnalysis . Вы также можете использовать специальную Surface для предварительного просмотра камеры, что обеспечивает большую гибкость, тогда как с CameraController вам необходимо использовать PreviewView . Использование существующего кода Surface может быть полезным, если он уже является входными данными для других частей вашего приложения.

    Вы настраиваете варианты использования с помощью методов set() и завершаете их с помощью метода build() . Каждый объект варианта использования предоставляет набор API-интерфейсов для конкретного варианта использования. Например, вариант использования захвата изображений предусматривает вызов метода takePicture() .

    Вместо того, чтобы приложение помещало определенные вызовы методов запуска и остановки в onResume() и onPause() , приложение определяет жизненный цикл, с которым нужно связать камеру, используя cameraProvider.bindToLifecycle() . Затем этот жизненный цикл сообщает CameraX, когда следует настроить сеанс захвата камеры, и обеспечивает соответствующие изменения состояния камеры в соответствии с переходами жизненного цикла.

    Шаги реализации для каждого варианта использования см. в разделах «Реализация предварительного просмотра» , «Анализ изображений» , «Захват изображений» и «Захват видео».

    Вариант использования предварительного просмотра взаимодействует с Surface для отображения. Приложения создают вариант использования с параметрами конфигурации, используя следующий код:

    Котлин

    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())
    

    Ява

    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();
    

    Дополнительные примеры кода см. в официальном примере приложения CameraX .

    Жизненный цикл CameraX

    CameraX наблюдает за жизненным циклом, чтобы определить, когда открывать камеру, когда создавать сеанс захвата, а когда останавливаться и выключаться. API вариантов использования предоставляют вызовы методов и обратные вызовы для отслеживания прогресса.

    Как поясняется в разделе «Объединение вариантов использования» , вы можете связать некоторые сочетания вариантов использования с одним жизненным циклом. Если вашему приложению необходимо поддерживать варианты использования, которые невозможно объединить, вы можете выполнить одно из следующих действий:

    • Группируйте совместимые варианты использования в несколько фрагментов , а затем переключайтесь между фрагментами.
    • Создайте собственный компонент жизненного цикла и используйте его для ручного управления жизненным циклом камеры.

    Если вы отделяете владельцев жизненного цикла вариантов использования представления и камеры (например, если вы используете пользовательский жизненный цикл или фрагмент сохранения ), то вы должны убедиться, что все варианты использования отвязаны от CameraX с помощью ProcessCameraProvider.unbindAll() или путем отмены привязки. каждый вариант использования индивидуально. Альтернативно, когда вы привязываете варианты использования к жизненному циклу, вы можете позволить CameraX управлять открытием и закрытием сеанса захвата, а также отменой привязки вариантов использования.

    Если все функции вашей камеры соответствуют жизненному циклу одного компонента, учитывающего жизненный цикл, такого как AppCompatActivity или фрагмент AppCompat , то использование жизненного цикла этого компонента при привязке всех желаемых вариантов использования гарантирует, что функциональность камеры будет готова, когда Компонент с учетом жизненного цикла активен и безопасно удаляется, в противном случае не потребляя никаких ресурсов.

    Пользовательские владельцы жизненного цикла

    В сложных случаях вы можете создать собственный LifecycleOwner , чтобы ваше приложение могло явно управлять жизненным циклом сеанса CameraX вместо привязки его к стандартному LifecycleOwner для Android.

    В следующем примере кода показано, как создать простой пользовательский LifecycleOwner:

    Котлин

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

    Ява

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

    Используя этот LifecycleOwner , ваше приложение может размещать переходы между состояниями в нужных точках своего кода. Дополнительные сведения о реализации этой функции в вашем приложении см. в разделе «Реализация пользовательского LifecycleOwner» .

    Параллельные варианты использования

    Варианты использования могут выполняться одновременно. Хотя варианты использования могут быть последовательно привязаны к жизненному циклу, лучше связать все варианты использования одним вызовом CameraProcessProvider.bindToLifecycle() . Дополнительные сведения о рекомендациях по изменению конфигурации см. в разделе Обработка изменений конфигурации .

    В следующем примере кода приложение указывает два варианта использования, которые необходимо создать и запустить одновременно. Он также определяет жизненный цикл, который будет использоваться для обоих вариантов использования, чтобы они запускались и останавливались в соответствии с жизненным циклом.

    Котлин

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

    Ява

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

    Гарантированно поддерживаются следующие комбинации конфигураций (когда требуется предварительный просмотр или захват видео, но не оба одновременно):

    Предварительный просмотр или видеозахват Захват изображения Анализ Описания
    Предоставьте пользователю предварительный просмотр или запись видео, сделайте снимок и проанализируйте поток изображений.
    Сделайте снимок и проанализируйте поток изображений.
    Предоставьте пользователю предварительный просмотр или запись видео и сделайте снимок.
    Предоставьте пользователю предварительный просмотр или запись видео, а также анализируйте поток изображений.

    Если требуются и предварительный просмотр, и захват видео, условно поддерживаются следующие комбинации вариантов использования:

    Предварительный просмотр Захват видео Захват изображения Анализ Особое требование
    Гарантия на все камеры.
    LIMITED (или лучшее) устройство камеры.
    Устройство камеры УРОВНЯ_3 (или выше).

    Кроме того,

    • Каждый вариант использования может работать сам по себе. Например, приложение может записывать видео без предварительного просмотра.
    • Если расширения включены, гарантированно будет работать только комбинация ImageCapture и Preview . В зависимости от реализации OEM может оказаться невозможным добавить ImageAnalysis ; расширения не могут быть включены для варианта использования VideoCapture . Дополнительную информацию см. в справочном документе по расширению .
    • В зависимости от возможностей камеры некоторые камеры могут поддерживать эту комбинацию в режимах с более низким разрешением, но не могут поддерживать ту же комбинацию в некоторых более высоких разрешениях.

    Поддерживаемый уровень оборудования можно получить из Camera2CameraInfo . Например, следующий код проверяет, является ли задняя камера по умолчанию устройством LEVEL_3 :

    Котлин

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

    Ява

    @androidx.annotation.OptIn(markerClass = ExperimentalCamera2Interop.class)
    Boolean isBackCameraLevel3Device(ProcessCameraProvider cameraProvider) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            List\ 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;
    }
    

    Разрешения

    Вашему приложению потребуется разрешение CAMERA . Для сохранения изображений в файлы также потребуется разрешение WRITE_EXTERNAL_STORAGE , за исключением устройств под управлением Android 10 или более поздней версии.

    Дополнительные сведения о настройке разрешений для вашего приложения см. в разделе Запрос разрешений приложения .

    Требования

    CameraX имеет следующие минимальные требования к версии:

    • Android API уровня 21
    • Компоненты архитектуры Android 1.1.1

    Для действий с учетом жизненного цикла используйте FragmentActivity или AppCompatActivity .

    Объявить зависимости

    Чтобы добавить зависимость от CameraX, вам необходимо добавить в свой проект репозиторий Google Maven .

    Откройте файл settings.gradle для своего проекта и добавьте репозиторий google() как показано ниже:

    классный

    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            google()
            mavenCentral()
        }
    }
    

    Котлин

    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            google()
            mavenCentral()
        }
    }
    

    Добавьте следующее в конец блока Android:

    классный

    android {
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
        // For Kotlin projects
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
    

    Котлин

    android {
        compileOptions {
            sourceCompatibility = JavaVersion.VERSION_1_8
            targetCompatibility = JavaVersion.VERSION_1_8
        }
        // For Kotlin projects
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
    

    Добавьте следующее в файл build.gradle каждого модуля для приложения:

    классный

    dependencies {
      // CameraX core library using the camera2 implementation
      def camerax_version = "1.5.0-alpha01"
      // 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}"
    }

    Котлин

    dependencies {
        // CameraX core library using the camera2 implementation
        val camerax_version = "1.5.0-alpha01"
        // 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}")
    }

    Дополнительные сведения о настройке приложения в соответствии с этими требованиями см. в разделе Объявление зависимостей .

    Совместимость CameraX с Camera2

    CameraX построен на базе Camera2, а CameraX предоставляет способы чтения и даже записи свойств в реализации Camera2. Полную информацию см. в пакете Interop .

    Для получения дополнительных сведений о том, как CameraX настроила свойства Camera2, используйте Camera2CameraInfo для чтения базового CameraCharacteristics . Вы также можете записать базовые свойства Camera2 одним из следующих двух способов:

    В следующем примере кода используются варианты использования потока для оптимизации видеовызова. Используйте Camera2CameraInfo чтобы узнать, доступен ли вариант использования потока видеовызовов. Затем используйте Camera2Interop.Extender , чтобы установить вариант использования базового потока.

    Котлин

    // 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)
    

    Ява

    // 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)
    

    Дополнительные ресурсы

    Чтобы узнать больше о CameraX, обратитесь к следующим дополнительным ресурсам.

    Кодлаб

  • Начало работы с CameraX
  • Пример кода

  • Примеры приложений CameraX
  • ,

    На этой странице рассказывается об архитектуре CameraX, включая ее структуру, работу с API, работу с жизненными циклами и объединение вариантов использования.

    Структура CameraX

    Вы можете использовать CameraX для взаимодействия с камерой устройства через абстракцию, называемую вариантом использования. Доступны следующие варианты использования:

    • Preview : принимает поверхность для отображения предварительного просмотра, например PreviewView .
    • Анализ изображений : предоставляет доступные ЦП буферы для анализа, например, для машинного обучения.
    • Захват изображения : захватывает и сохраняет фотографию.
    • Захват видео : захватывайте видео и аудио с помощью VideoCapture

    Варианты использования можно комбинировать и активировать одновременно. Например, приложение может позволить пользователю просматривать изображение, которое видит камера, используя вариант использования предварительного просмотра, иметь вариант использования для анализа изображений, который определяет, улыбаются ли люди на фотографии, и включать вариант использования для захвата изображения, чтобы сделать снимок. раз они есть.

    Модель API

    Для работы с библиотекой вы указываете следующие вещи:

    • Желаемый вариант использования с опциями конфигурации.
    • Что делать с выходными данными, подключив прослушиватели.
    • Предполагаемый поток, например, когда включать камеры и когда создавать данные, путем привязки варианта использования к жизненным циклам архитектуры Android .

    Есть два способа написать приложение CameraX: CameraController (отлично, если вам нужен самый простой способ использования CameraX) или CameraProvider (отлично, если вам нужна большая гибкость).

    КамераКонтроллер

    CameraController обеспечивает большую часть основных функций CameraX в одном классе. Он требует небольшого количества кода настройки и автоматически выполняет инициализацию камеры, управление вариантами использования, вращение цели, фокусировку касанием, масштабирование пальцами и многое другое. Конкретным классом, расширяющим CameraController является LifecycleCameraController .

    Котлин

    val previewView: PreviewView = viewBinding.previewView
    var cameraController = LifecycleCameraController(baseContext)
    cameraController.bindToLifecycle(this)
    cameraController.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
    previewView.controller = cameraController
    

    Ява

    PreviewView previewView = viewBinding.previewView;
    LifecycleCameraController cameraController = new LifecycleCameraController(baseContext);
    cameraController.bindToLifecycle(this);
    cameraController.setCameraSelector(CameraSelector.DEFAULT_BACK_CAMERA);
    previewView.setController(cameraController);
    

    UseCase по умолчанию для CameraControllerPreview , ImageCapture и ImageAnalysis . Чтобы отключить ImageCapture или ImageAnalysis или включить VideoCapture , используйте метод setEnabledUseCases() .

    Дополнительные сведения об использовании CameraController см. в примере сканера QR-кода или в видеоролике об основах CameraController .

    Поставщик камеры

    CameraProvider по-прежнему прост в использовании, но поскольку большую часть настройки берет на себя разработчик приложения, появляется больше возможностей для настройки конфигурации, например включение вращения выходного изображения или установка формата выходного изображения в ImageAnalysis . Вы также можете использовать специальную Surface для предварительного просмотра камеры, что обеспечивает большую гибкость, тогда как с CameraController вам необходимо использовать PreviewView . Использование существующего кода Surface может быть полезным, если он уже является входными данными для других частей вашего приложения.

    Вы настраиваете варианты использования с помощью методов set() и завершаете их с помощью метода build() . Каждый объект варианта использования предоставляет набор API-интерфейсов для конкретного варианта использования. Например, вариант использования захвата изображений предусматривает вызов метода takePicture() .

    Вместо того, чтобы приложение помещало определенные вызовы методов запуска и остановки в onResume() и onPause() , приложение определяет жизненный цикл, с которым нужно связать камеру, используя cameraProvider.bindToLifecycle() . Затем этот жизненный цикл сообщает CameraX, когда следует настроить сеанс захвата камеры, и обеспечивает соответствующие изменения состояния камеры в соответствии с переходами жизненного цикла.

    Шаги реализации для каждого варианта использования см. в разделах «Реализация предварительного просмотра» , «Анализ изображений» , «Захват изображений» и «Захват видео».

    Вариант использования предварительного просмотра взаимодействует с Surface для отображения. Приложения создают вариант использования с параметрами конфигурации, используя следующий код:

    Котлин

    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())
    

    Ява

    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();
    

    Дополнительные примеры кода см. в официальном примере приложения CameraX .

    Жизненный цикл CameraX

    CameraX наблюдает за жизненным циклом, чтобы определить, когда открывать камеру, когда создавать сеанс захвата, а когда останавливаться и выключаться. API вариантов использования предоставляют вызовы методов и обратные вызовы для отслеживания прогресса.

    Как поясняется в разделе «Объединение вариантов использования» , вы можете связать некоторые сочетания вариантов использования с одним жизненным циклом. Если вашему приложению необходимо поддерживать варианты использования, которые невозможно объединить, вы можете выполнить одно из следующих действий:

    • Группируйте совместимые варианты использования в несколько фрагментов , а затем переключайтесь между фрагментами.
    • Создайте собственный компонент жизненного цикла и используйте его для ручного управления жизненным циклом камеры.

    Если вы отделяете владельцев жизненного цикла вариантов использования представления и камеры (например, если вы используете пользовательский жизненный цикл или фрагмент сохранения ), то вы должны убедиться, что все варианты использования отвязаны от CameraX с помощью ProcessCameraProvider.unbindAll() или путем отмены привязки. каждый вариант использования индивидуально. Альтернативно, когда вы привязываете варианты использования к жизненному циклу, вы можете позволить CameraX управлять открытием и закрытием сеанса захвата, а также отменой привязки вариантов использования.

    Если все функции вашей камеры соответствуют жизненному циклу одного компонента, учитывающего жизненный цикл, такого как AppCompatActivity или фрагмент AppCompat , то использование жизненного цикла этого компонента при привязке всех желаемых вариантов использования гарантирует, что функциональность камеры будет готова, когда Компонент с учетом жизненного цикла активен и безопасно удаляется, в противном случае не потребляя никаких ресурсов.

    Пользовательские владельцы жизненного цикла

    В сложных случаях вы можете создать собственный LifecycleOwner , чтобы ваше приложение могло явно управлять жизненным циклом сеанса CameraX вместо привязки его к стандартному LifecycleOwner для Android.

    В следующем примере кода показано, как создать простой пользовательский LifecycleOwner:

    Котлин

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

    Ява

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

    Используя этот LifecycleOwner , ваше приложение может размещать переходы между состояниями в нужных точках своего кода. Дополнительные сведения о реализации этой функции в вашем приложении см. в разделе «Реализация пользовательского LifecycleOwner» .

    Параллельные варианты использования

    Варианты использования могут выполняться одновременно. Хотя варианты использования могут быть последовательно привязаны к жизненному циклу, лучше связать все варианты использования одним вызовом CameraProcessProvider.bindToLifecycle() . Дополнительные сведения о рекомендациях по изменению конфигурации см. в разделе Обработка изменений конфигурации .

    В следующем примере кода приложение указывает два варианта использования, которые необходимо создать и запустить одновременно. Он также определяет жизненный цикл, который будет использоваться для обоих вариантов использования, чтобы они запускались и останавливались в соответствии с жизненным циклом.

    Котлин

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

    Ява

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

    Гарантированно поддерживаются следующие комбинации конфигураций (когда требуется предварительный просмотр или захват видео, но не оба одновременно):

    Предварительный просмотр или видеозахват Захват изображения Анализ Описания
    Предоставьте пользователю предварительный просмотр или запись видео, сделайте снимок и проанализируйте поток изображений.
    Сделайте снимок и проанализируйте поток изображений.
    Предоставьте пользователю предварительный просмотр или запись видео и сделайте снимок.
    Предоставьте пользователю предварительный просмотр или запись видео, а также анализируйте поток изображений.

    Если требуются и предварительный просмотр, и захват видео, условно поддерживаются следующие комбинации вариантов использования:

    Предварительный просмотр Захват видео Захват изображения Анализ Особое требование
    Гарантия на все камеры.
    LIMITED (или лучшее) устройство камеры.
    Устройство камеры УРОВНЯ_3 (или выше).

    Кроме того,

    • Каждый вариант использования может работать сам по себе. Например, приложение может записывать видео без предварительного просмотра.
    • Если расширения включены, гарантированно будет работать только комбинация ImageCapture и Preview . В зависимости от реализации OEM может оказаться невозможным добавить ImageAnalysis ; расширения не могут быть включены для варианта использования VideoCapture . Дополнительную информацию см. в справочном документе по расширению .
    • В зависимости от возможностей камеры некоторые камеры могут поддерживать эту комбинацию в режимах с более низким разрешением, но не могут поддерживать ту же комбинацию в некоторых более высоких разрешениях.

    Поддерживаемый уровень оборудования можно получить из Camera2CameraInfo . Например, следующий код проверяет, является ли задняя камера по умолчанию устройством LEVEL_3 :

    Котлин

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

    Ява

    @androidx.annotation.OptIn(markerClass = ExperimentalCamera2Interop.class)
    Boolean isBackCameraLevel3Device(ProcessCameraProvider cameraProvider) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            List\ 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;
    }
    

    Разрешения

    Вашему приложению потребуется разрешение CAMERA . Для сохранения изображений в файлы также потребуется разрешение WRITE_EXTERNAL_STORAGE , за исключением устройств под управлением Android 10 или более поздней версии.

    Дополнительные сведения о настройке разрешений для вашего приложения см. в разделе Запрос разрешений приложения .

    Требования

    CameraX имеет следующие минимальные требования к версии:

    • Android API уровня 21
    • Компоненты архитектуры Android 1.1.1

    Для действий с учетом жизненного цикла используйте FragmentActivity или AppCompatActivity .

    Объявить зависимости

    Чтобы добавить зависимость от CameraX, вам необходимо добавить в свой проект репозиторий Google Maven .

    Откройте файл settings.gradle для своего проекта и добавьте репозиторий google() как показано ниже:

    классный

    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            google()
            mavenCentral()
        }
    }
    

    Котлин

    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            google()
            mavenCentral()
        }
    }
    

    Добавьте следующее в конец блока Android:

    классный

    android {
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
        // For Kotlin projects
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
    

    Котлин

    android {
        compileOptions {
            sourceCompatibility = JavaVersion.VERSION_1_8
            targetCompatibility = JavaVersion.VERSION_1_8
        }
        // For Kotlin projects
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
    

    Добавьте следующее в файл build.gradle каждого модуля для приложения:

    классный

    dependencies {
      // CameraX core library using the camera2 implementation
      def camerax_version = "1.5.0-alpha01"
      // 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}"
    }

    Котлин

    dependencies {
        // CameraX core library using the camera2 implementation
        val camerax_version = "1.5.0-alpha01"
        // 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}")
    }

    Дополнительные сведения о настройке приложения в соответствии с этими требованиями см. в разделе Объявление зависимостей .

    Совместимость CameraX с Camera2

    CameraX построен на базе Camera2, а CameraX предоставляет способы чтения и даже записи свойств в реализации Camera2. Полную информацию см. в пакете Interop .

    Для получения дополнительных сведений о том, как CameraX настроила свойства Camera2, используйте Camera2CameraInfo для чтения базового CameraCharacteristics . Вы также можете записать базовые свойства Camera2 одним из следующих двух способов:

    В следующем примере кода используются варианты использования потока для оптимизации видеовызова. Используйте Camera2CameraInfo чтобы узнать, доступен ли вариант использования потока видеовызовов. Затем используйте Camera2Interop.Extender , чтобы установить вариант использования базового потока.

    Котлин

    // 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)
    

    Ява

    // 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)
    

    Дополнительные ресурсы

    Чтобы узнать больше о CameraX, обратитесь к следующим дополнительным ресурсам.

    Кодлаб

  • Начало работы с CameraX
  • Пример кода

  • Примеры приложений CameraX