Cấu trúc CameraX

Trang này đề cập đến cấu trúc của CameraX, bao gồm cấu trúc, cách làm việc với API, cách làm việc với vòng đời và cách kết hợp các trường hợp sử dụng.

Cấu trúc CameraX

Bạn có thể sử dụng CameraX để giao tiếp với máy ảnh của thiết bị thông qua một giá trị trừu tượng được gọi là trường hợp sử dụng. Bạn có thể sử dụng các trường hợp sau:

  • Xem trước: chấp nhận một nền tảng để hiển thị bản xem trước, chẳng hạn như PreviewView.
  • Phân tích hình ảnh: cung cấp vùng đệm mà CPU có thể truy cập để phân tích, chẳng hạn như cho công nghệ máy học.
  • Chụp ảnh: chụp và lưu ảnh.
  • Quay video: quay video và ghi âm bằng VideoCapture

Các trường hợp sử dụng có thể được kết hợp và hoạt động đồng thời. Ví dụ: Một ứng dụng có thể cho phép người dùng xem hình ảnh mà máy ảnh thấy bằng trường hợp sử dụng xem trước, có trường hợp sử dụng phân tích hình ảnh xác định liệu những người trong ảnh có đang cười hay không và đưa vào một trường hợp sử dụng chụp ảnh để chụp ảnh khi mọi người đã sẵn sàng.

Mô hình API

Để làm việc với thư viện, bạn cần chỉ định những điều sau:

  • Trường hợp sử dụng mong muốn với các tuỳ chọn cấu hình.
  • Việc cần làm với dữ liệu đầu ra bằng cách đính kèm trình nghe.
  • Quy trình dự kiến, chẳng hạn như thời điểm bật máy ảnh và thời điểm tạo dữ liệu, bằng cách liên kết trường hợp sử dụng với Vòng đời kiến trúc Android.

Bạn định cấu hình các trường hợp sử dụng bằng các phương thức set() và hoàn tất các trường hợp sử dụng đó bằng phương thức build(). Mỗi đối tượng trường hợp sử dụng cung cấp một tập hợp API dành riêng cho trường hợp sử dụng. Ví dụ: Trường hợp sử dụng chụp ảnh cung cấp lệnh gọi phương thức takePicture().

Thay vì thực hiện các lệnh gọi phương thức bắt đầu và dừng cụ thể trong onResume()onPause(), ứng dụng này chỉ định vòng đời để liên kết máy ảnh bằng cách sử dụng cameraProvider.bindToLifecycle(). Sau đó, vòng đời thông báo cho CameraX về thời điểm cần định cấu hình phiên chụp ảnh và đảm bảo trạng thái của máy ảnh thay đổi phù hợp để khớp với các chuyển đổi vòng đời.

Để biết các bước triển khai cho từng trường hợp sử dụng, xem phần Triển khai bản xem trước, Phân tích hình ảnh, Chụp ảnhQuay video

Ví dụ về mô hình API

Trường hợp sử dụng xem trước tương tác với một Surface để hiển thị. Ứng dụng tạo trường hợp sử dụng với các tuỳ chọn cấu hình bằng mã sau:

Kotlin

val preview = Preview.Builder().build()
val viewFinder: PreviewView = findViewById(R.id.previewView)

// The use case is bound to an Android Lifecycle with the following code
val camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// PreviewView creates a surface provider and is the recommended provider
preview.setSurfaceProvider(viewFinder.getSurfaceProvider())

Java

Preview preview = new Preview.Builder().build();
PreviewView viewFinder = findViewById(R.id.view_finder);

// The use case is bound to an Android Lifecycle with the following code
Camera camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview);

// PreviewView creates a surface provider, using a Surface from a different
// kind of view will require you to implement your own surface provider.
preview.previewSurfaceProvider = viewFinder.getSurfaceProvider();

Để biết thêm mã ví dụ, xem ứng dụng mẫu CameraX chính thức.

CameraX Lifecycle

CameraX theo dõi vòng đời để xác định thời điểm mở máy ảnh, thời điểm tạo phiên quay chụp, cũng như thời điểm dừng và tắt. API trường hợp sử dụng cung cấp các lệnh gọi phương thức và lệnh gọi lại để theo dõi tiến trình.

Như được giải thích trong phần Kết hợp các trường hợp sử dụng, bạn có thể liên kết một số danh sách kết hợp các trường hợp sử dụng với một vòng đời duy nhất. Khi ứng dụng của bạn cần hỗ trợ các trường hợp sử dụng không thể kết hợp được, bạn có thể thực hiện một trong những thao tác sau:

  • Nhóm các trường hợp sử dụng tương thích lại với nhau thành nhiều mảnh, sau đó chuyển đổi giữa các mảnh
  • Tạo một thành phần trong vòng đời tuỳ chỉnh và sử dụng thành phần đó để điều khiển vòng đời của máy ảnh theo cách thủ công

Nếu bạn tách riêng các chủ sở hữu Lifecycle của trường hợp sử dụng xem và máy ảnh (ví dụ: nếu bạn sử dụng vòng đời tuỳ chỉnh hoặc mảnh giữ lại), bạn phải đảm bảo rằng tất cả các trường hợp sử dụng huỷ liên kết với CameraX bằng cách sử dụng ProcessCameraProvider.unbindAll() hoặc bằng cách huỷ liên kết từng trường hợp sử dụng một. Ngoài ra, khi liên kết các trường hợp sử dụng với Lifecycle, bạn có thể cho phép CameraX quản lý việc mở và đóng phiên quay chụp và huỷ liên kết các trường hợp sử dụng.

Nếu tất cả chức năng của máy ảnh tương ứng với vòng đời của một thành phần nhận biết vòng đời duy nhất, chẳng hạn như AppCompatActivity hoặc một mảnh AppCompat, thì việc sử dụng vòng đời của thành phần đó khi liên kết tất cả các trường hợp sử dụng mong muốn sẽ đảm bảo rằng chức năng của máy ảnh đã sẵn sàng khi thành phần nhận biết vòng đời hoạt động và được xử lý một cách an toàn, không tốn bất kỳ tài nguyên nào.

LifecycleOwner tuỳ chỉnh

Đối với các trường hợp nâng cao, bạn có thể tạo một LifecycleOwner tuỳ chỉnh để cho phép ứng dụng của bạn kiểm soát rõ ràng vòng đời phiên CameraX thay vì liên kết với Android tiêu chuẩnLifecycleOwner.

Mã mẫu sau đây trình bày cách tạo một LifecycleOwner tuỳ chỉnh đơn giản:

Kotlin

class CustomLifecycle : LifecycleOwner {
    private val lifecycleRegistry: LifecycleRegistry

    init {
        lifecycleRegistry = LifecycleRegistry(this);
        lifecycleRegistry.markState(Lifecycle.State.CREATED)
    }
    ...
    fun doOnResume() {
        lifecycleRegistry.markState(State.RESUMED)
    }
    ...
    override fun getLifecycle(): Lifecycle {
        return lifecycleRegistry
    }
}

Java

public class CustomLifecycle implements LifecycleOwner {
    private LifecycleRegistry lifecycleRegistry;
    public CustomLifecycle() {
        lifecycleRegistry = new LifecycleRegistry(this);
        lifecycleRegistry.markState(Lifecycle.State.CREATED);
    }
   ...
   public void doOnResume() {
        lifecycleRegistry.markState(State.RESUMED);
    }
   ...
    public Lifecycle getLifecycle() {
        return lifecycleRegistry;
    }
}

Khi sử dụng LifecycleOwner này, ứng dụng của bạn có thể đặt các lượt chuyển đổi trạng thái tại các điểm mong muốn trong mã. Để biết thêm thông tin về cách triển khai chức năng này trong ứng dụng của bạn, hãy xem phần Triển khai một LifecycleOwner tuỳ chỉnh.

Các trường hợp sử dụng đồng thời

Các trường hợp sử dụng có thể chạy đồng thời. Mặc dù các trường hợp sử dụng có thể được liên kết tuần tự với một vòng đời, nhưng bạn nên liên kết tất cả các trường hợp sử dụng bằng một lệnh gọi tới CameraProcessProvider.bindToLifecycle(). Để biết thêm thông tin về các phương pháp hay nhất khi thay đổi cấu hình, xem phần Xử lý các thay đổi về cấu hình.

Trong mã mẫu sau, ứng dụng chỉ định hai trường hợp sử dụng cần được tạo và chạy đồng thời. Mã mẫu này cũng xác định vòng đời cần sử dụng cho cả hai trường hợp sử dụng, để cả hai đều bắt đầu và dừng theo vòng đời đó.

Kotlin

private lateinit var imageCapture: ImageCapture

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener(Runnable {
        // Camera provider is now guaranteed to be available
        val cameraProvider = cameraProviderFuture.get()

        // Set up the preview use case to display camera preview.
        val preview = Preview.Builder().build()

        // Set up the capture use case to allow users to take photos.
        imageCapture = ImageCapture.Builder()
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .build()

        // Choose the camera by requiring a lens facing
        val cameraSelector = CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_FRONT)
                .build()

        // Attach use cases to the camera with the same lifecycle owner
        val camera = cameraProvider.bindToLifecycle(
                this as LifecycleOwner, cameraSelector, preview, imageCapture)

        // Connect the preview use case to the previewView
        preview.setSurfaceProvider(
                previewView.getSurfaceProvider())
    }, ContextCompat.getMainExecutor(this))
}

Java

private ImageCapture imageCapture;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    PreviewView previewView = findViewById(R.id.previewView);

    ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
            ProcessCameraProvider.getInstance(this);

    cameraProviderFuture.addListener(() -> {
        try {
            // Camera provider is now guaranteed to be available
            ProcessCameraProvider cameraProvider = cameraProviderFuture.get();

            // Set up the view finder use case to display camera preview
            Preview preview = new Preview.Builder().build();

            // Set up the capture use case to allow users to take photos
            imageCapture = new ImageCapture.Builder()
                    .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                    .build();

            // Choose the camera by requiring a lens facing
            CameraSelector cameraSelector = new CameraSelector.Builder()
                    .requireLensFacing(lensFacing)
                    .build();

            // Attach use cases to the camera with the same lifecycle owner
            Camera camera = cameraProvider.bindToLifecycle(
                    ((LifecycleOwner) this),
                    cameraSelector,
                    preview,
                    imageCapture);

            // Connect the preview use case to the previewView
            preview.setSurfaceProvider(
                    previewView.getSurfaceProvider());
        } catch (InterruptedException | ExecutionException e) {
            // Currently no exceptions thrown. cameraProviderFuture.get()
            // shouldn't block since the listener is being called, so no need to
            // handle InterruptedException.
        }
    }, ContextCompat.getMainExecutor(this));
}

Chúng tôi đảm bảo hỗ trợ các tổ hợp cấu hình sau đây (khi yêu cầu trường hợp sử dụng Xem trước hoặc Quay video, nhưng không yêu cầu cả hai cùng một lúc):

Xem trước hoặc Quay video Chụp ảnh Phân tích Mô tả
Cung cấp cho người dùng tính năng xem trước hoặc quay video, chụp ảnh và phân tích luồng hình ảnh.
  Chụp ảnh và phân tích luồng hình ảnh.
  Cung cấp cho người dùng tính năng xem trước hoặc quay video, chụp ảnh.
  Cung cấp cho người dùng tính năng xem trước hoặc quay video, cũng như phân tích luồng hình ảnh.

Khi yêu cầu cả Xem trước và Quay video, các tổ hợp trường hợp sử dụng sau đây được hỗ trợ theo điều kiện:

Xem trước Quay video Chụp ảnh Phân tích Yêu cầu đặc biệt
    Được đảm bảo cho tất cả máy ảnh
  Thiết bị máy ảnh LIMITED (hoặc cao hơn).
  Thiết bị máy ảnh LEVEL_3 (hoặc cao hơn).

Ngoài ra,

  • Mỗi trường hợp sử dụng đều có thể tự hoạt động. Ví dụ: Một ứng dụng có thể quay video mà không cần sử dụng tính năng xem trước.
  • Khi các tiện ích được bật, chỉ tổ hợp ImageCapturePreview được đảm bảo hoạt động. Tuỳ thuộc vào cách triển khai của OEM (Nhà sản xuất thiết bị gốc), bạn cũng có thể không thêm được ImageAnalysis; không thể bật tiện ích cho trường hợp sử dụng VideoCapture. Xem Tài liệu tham khảo về tiện ích để biết thông tin chi tiết.
  • Tuỳ thuộc vào tính năng của máy ảnh, một số máy ảnh có thể hỗ trợ tổ hợp ở các chế độ độ phân giải thấp hơn nhưng không thể hỗ trợ sử dụng cùng một tổ hợp ở một số độ phân giải cao hơn.

Bạn có thể truy xuất cấp độ phần cứng được hỗ trợ từ Camera2CameraInfo. Ví dụ: Mã sau đây kiểm tra xem máy ảnh mặt sau mặc định có phải là thiết bị LEVEL_3 không:

Kotlin

@androidx.annotation.OptIn(ExperimentalCamera2Interop::class)
fun isBackCameraLevel3Device(cameraProvider: ProcessCameraProvider) : Boolean {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        return CameraSelector.DEFAULT_BACK_CAMERA
            .filter(cameraProvider.availableCameraInfos)
            .firstOrNull()
            ?.let { Camera2CameraInfo.from(it) }
            ?.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) ==
            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
    }
    return false
}

Java

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

Quyền

Ứng dụng của bạn sẽ cần được cấp quyền CAMERA. Để lưu hình ảnh vào tệp, bạn cũng cần có quyền WRITE_EXTERNAL_STORAGE, ngoại trừ trên các thiết bị chạy Android 10 trở lên.

Để biết thêm thông tin về cách định cấu hình quyền cho ứng dụng của bạn, đọc bài viết Yêu cầu quyền cho ứng dụng.

Yêu cầu

CameraX có các yêu cầu tối thiểu về phiên bản sau đây:

  • Android API cấp 21
  • Bộ thành phần cấu trúc Android 1.1.1

Đối với các hoạt động nhận biết vòng đời, sử dụng FragmentActivity hoặc AppCompatActivity.

Khai báo phần phụ thuộc

Để thêm một phần phụ thuộc trên CameraX, bạn phải thêm kho lưu trữ Google Maven vào dự án.

Mở tệp settings.gradle cho dự án của bạn và thêm kho lưu trữ google() như trong mã sau:

Groovy

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

Kotlin

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

Thêm mã sau vào cuối khối lệnh Android:

Groovy

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

Kotlin

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

Thêm phần sau vào tệp build.gradle của mỗi mô-đun cho một ứng dụng:

Groovy

dependencies {
  // CameraX core library using the camera2 implementation
  def camerax_version = "1.2.0-alpha04"
  // The following line is optional, as the core library is included indirectly by camera-camera2
  implementation "androidx.camera:camera-core:${camerax_version}"
  implementation "androidx.camera:camera-camera2:${camerax_version}"
  // If you want to additionally use the CameraX Lifecycle library
  implementation "androidx.camera:camera-lifecycle:${camerax_version}"
  // If you want to additionally use the CameraX VideoCapture library
  implementation "androidx.camera:camera-video:${camerax_version}"
  // If you want to additionally use the CameraX View class
  implementation "androidx.camera:camera-view:${camerax_version}"
  // If you want to additionally add CameraX ML Kit Vision Integration
  implementation "androidx.camera:camera-mlkit-vision:${camerax_version}"
  // If you want to additionally use the CameraX Extensions library
  implementation "androidx.camera:camera-extensions:${camerax_version}"
}

Kotlin

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

Để biết thêm thông tin về cách định cấu hình ứng dụng của bạn nhằm tuân thủ các yêu cầu này, hãy xem bài viết Khai báo phần phụ thuộc.

Tài nguyên khác

Để tìm hiểu thêm về CameraX, hãy tham khảo các tài nguyên bổ sung sau đây.

Lớp học lập trình

  • Bắt đầu sử dụng CameraX
  • Mã mẫu

  • Ứng dụng mẫu CameraX