CameraX 架構

本頁面介紹 CameraX 的架構,包括其結構、搭配 API 使用的方式、生命週期,以及合併用途的方式。

CameraX 結構

您可以使用 CameraX,利用稱為用途的抽象層來連結裝置的相機。幾種可使用的用途如下:

  • 預覽:可藉由介面顯示預覽畫面,例如 PreviewView
  • 相片分析:提供 CPU 可存取的緩衝區進行分析,例如機器學習。
  • 相片拍攝:拍攝並儲存相片。
  • 影片拍攝:使用 VideoCapture 錄製影片和音訊

這些功能可以合併,並且同時啟用。舉例來說,應用程式可讓使用者透過預覽功能,看見相機所拍攝的圖片,也能使用圖片分析功能,判斷相片中的人物是否在微笑,並在偵測到笑容時進行拍照。

API 模型

如要使用這個程式庫,請指定下列項目:

  • 搭配設定選項的期望功能。
  • 如何透過附加事件監聽器來處理輸出資料。
  • 將用途綁定至 Android 架構生命週期後的預期流程,例如啟用相機和產生資料的時機。

您可以使用 set() 方法設定功能,並透過 build() 方法完成功能。每個功能物件都有一組功能專屬的 API。例如,圖片拍攝功能提供 takePicture() 呼叫方法。

應用程式不會在 onResume()onPause() 中放置特定的啟動和停止方法呼叫,而是使用 cameraProvider.bindToLifecycle() 來指定要與相機建立關聯的生命週期。隨後,該生命週期會告知 CameraX 何時應設定相機的拍攝工作階段,並確保相機狀態隨著生命週期的轉換而適時變更。

如需各種功能的實作步驟,請參閱「實作預覽」「分析圖片」「相片拍攝」以及「影片拍攝」

API 模型範例

預覽功能會與 Surface 互動以顯示畫面。如需應用程式建立具有設定選項的功能,請見下列程式碼範例:

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

如需更多程式碼範例,請參閱 CameraX 官方範例應用程式

CameraX 生命週期

CameraX 會觀察生命週期,進而判斷何時開啟相機、建立拍攝工作階段,以及應中止和結束工作的時機。功能 API 提供方法呼叫和回呼來監控進度。

合併功能一節所述,部分功能可以綁定單一生命週期。如果應用程式需要提供無法合併的功能,請從下列做法中,擇一採用:

  • 將相容的用途劃分為多個片段,然後在片段之間切換
  • 建立自訂生命週期元件,並使用該元件手動控制相機的生命週期。

如果您將檢視畫面用途和相機用途的 LifecycleOwner 分離 (例如使用自訂生命週期或保留片段),則必須透過 ProcessCameraProvider.unbindAll() 或個別解除各個用途的繫結,藉此確保所有用途均已與 CameraX 解除繫結。您也可以將用途繫結至生命週期,讓 CameraX 管理拍攝工作階段的開始和結束作業,以及用途的解除繫結作業。

如果所有相機功能都與單一生命週期感知元件 (例如 AppCompatActivityAppCompat 片段) 的生命週期相對應,那麼只要在繫結至所有所需用途時使用該元件的生命週期,即可確保相機功能在單一生命週期感知元件啟用時準備就緒,並於該元件停用時受到妥善處置,而不會耗用任何資源。

自訂 LifecycleOwner

針對進階用途,開發人員可以建立自訂 LifecycleOwner,讓應用程式精確控管 CameraX 工作階段生命週期,而不是將其連結至標準 Android LifecycleOwner

以下的程式碼範例,將說明如何建立簡易的自訂 LifecycleOwner:

Kotlin

class CustomLifecycle : LifecycleOwner {
    private val lifecycleRegistry: LifecycleRegistry

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

Java

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

只要使用這個 LifecycleOwner,應用程式,即可在目標程式碼中,於所需時間點放置狀態轉換。如要進一步瞭解如何將這項功能導入應用程式,請參閱「導入自訂 LifecycleOwner」

並行功能

功能可以並行啟用。儘管功能可以依序綁定生命週期,但最好透過單一呼叫 CameraProcessProvider.bindToLifecycle(),綁定所有功能。如要進一步瞭解設定變更的最佳做法,請參閱「處理設定變更」一文。

如要在應用程式中,指定兩個功能同時建立並執行,請參閱下列程式碼範例。該範例同時指定了這兩種用途的生命週期,如此一來這兩個用途就會根據生命週期啟動及停止。

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

系統保證提供下列設定組合 (需要使用「預覽」或「影片拍攝」功能,但請勿同時使用):

預覽或錄影 相片拍攝 分析 說明
提供使用者預覽或錄影、拍照,搭配分析相片串流功能。
  拍照並搭配分析圖片串流功能。
  提供使用者預覽或錄影,並搭配拍照功能。
  提供使用者預覽或錄影,並搭配分析圖片串流功能。

如果需同時使用「預覽」和「錄影」功能,程式會視情況提供下列功能組合:

預覽 錄影 相片拍攝 分析 特殊條件
    適用所有攝影機
  限定相機,或更好的相機。
  LEVEL_3 或更高級別的相機。

此外

  • 各種功能都可獨立運作。舉例來說,應用程式可以不預覽只錄影。
  • 啟用擴充功能後,只有 ImageCapturePreview 組合可保證正常運作。由於原設備製造商 (OEM) 導入狀況各有不同,您可能無法新增 ImageAnalysisVideoCapture 功能無法擴充。詳情請查看「擴充功能參考文件」
  • 視相機功能而定,部分相機在低解析度模式和高解析度模式下支援的用途組合可能不盡相同。

您可以從 Camera2CameraInfo 取得支援的硬體級別。比如,以下程式碼會檢查預設後置鏡頭是否為 LEVEL_3 裝置:

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

權限

應用程式需要 CAMERA 權限。如要將圖片儲存至檔案,還須得到 WRITE_EXTERNAL_STORAGE 權限 (使用 Android 10 或以上版本的裝置除外)。

如要進一步瞭解如何設定應用程式的權限,請參閱「要求應用程式權限」

必要條件

CameraX 最低版本需求如下:

  • Android API 級別 21
  • Android 架構元件 1.1.1 版

針對生命週期感知活動,請使用 FragmentActivityAppCompatActivity

宣告依附元件

如要新增依附元件至 CameraX,在專案中必須新增 Google Maven 存放區

請按照以下範例,開啟專案的 settings.gradle 檔案,並新增 google() 存放區:

Groovy

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

Kotlin

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

請將以下內容新增到 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"
    }
}

請將以下程式碼加進各模組的 build.gradle 檔案:

Groovy

dependencies {
  // CameraX core library using the camera2 implementation
  def camerax_version = "1.2.0-rc01"
  // 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-rc01"
    // 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,請參閱下列其他資源。

程式碼研究室

  • 開始使用 CameraX
  • 程式碼範例

  • CameraX 範例應用程式