CameraX 아키텍처

CameraX는 Camera2 API의 기능을 쉽게 활용할 수 있게 해주는 Jetpack의 부가기능입니다. 이 주제에서는 CameraX의 구조, API의 작동 방식, 수명 주기 활용 방법, 사용 사례를 결합하는 방법을 포함하여 CameraX의 아키텍처를 다룹니다.

CameraX 구조

개발자는 CameraX를 사용하여 사용 사례라고 하는 추상화를 통해 기기의 카메라와 연결합니다. 현재 다음 사용 사례를 이용할 수 있습니다.

  • 미리보기: PreviewView 같은 미리보기를 표시할 영역을 허용합니다.
  • 이미지 분석: 머신러닝 등의 분석을 위해 CPU에서 액세스할 수 있는 버퍼를 제공합니다.
  • 이미지 캡처: 사진을 캡처하고 저장합니다.

사용 사례를 결합하고 동시에 활성화할 수 있습니다. 예를 들어 앱이 미리보기 사용 사례를 사용하여 카메라에 표시되는 이미지를 사용자가 볼 수 있게 하고, 이미지 분석 사용 사례를 통해 사진 속의 인물이 웃고 있는지 확인하고, 웃고 있는 경우 사진을 찍도록 이미지 캡처 사용 사례를 포함할 수 있습니다.

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

자바

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에서는 진행 상황을 모니터링할 메서드 호출과 콜백을 제공합니다.

사용 사례 결합에 설명된 것처럼 몇 가지 사용 사례 조합을 하나의 수명 주기로 결합할 수 있습니다. 앱에서 결합할 수 없는 사용 사례를 지원해야 하는 경우 다음 중 하나를 실행하세요.

  • 호환되는 사용 사례를 둘 이상의 프래그먼트로 그룹화하고 프래그먼트 간에 전환합니다.
  • 맞춤 수명 주기 구성요소를 만들고 이를 사용하여 카메라 수명 주기를 수동으로 관리합니다.

뷰와 카메라 사용 사례 수명 주기 소유자를 분리하는 경우(예: 맞춤 수명 주기 또는 유지 프래그먼트를 사용하는 경우) ProcessCameraProvider.unbindAll()을 사용하거나 각 사용 사례를 개별적으로 결합 해제하여 CameraX에서 모든 사용 사례의 결합이 해제되도록 해야 합니다. 또는 사용 사례를 수명 주기로 결합할 때 CameraX가 캡처 세션 열기 및 닫기와 사용 사례 결합 해제를 관리하도록 허용할 수 있습니다.

카메라의 모든 기능이 AppCompatActivity 또는 AppCompat 프래그먼트와 같은 단일 수명 주기 인식 구성요소의 수명 주기와 일치하는 경우, 원하는 모든 사용 사례 결합 시 이 구성요소의 수명 주기를 사용하면 수명 주기 인식 구성요소가 활성화되어 있을 때는 카메라 기능을 즉시 사용할 수 있고 활성화되어 있지 않을 때는 안전하게 해제되어 리소스를 소모하지 않게 됩니다.

맞춤 LifecycleOwners

고급 사용 사례의 경우 맞춤 LifecycleOwner를 만들어 앱이 표준 Android LifecycleOwner에 연결하지 않고 명시적으로 CameraX 세션 수명 주기를 관리하도록 할 수 있습니다.

다음 코드 샘플에서는 간단한 맞춤 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
    }
}

자바

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

자바

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

다음 구성 조합이 지원됩니다.

미리보기 분석 이미지 캡처 사용 사례 결합
사용자에게 미리보기를 제공하고 사진을 찍고 이미지 스트림을 분석합니다.
  사진을 찍고 이미지 스트림을 분석합니다.
  스트리밍되는 이미지의 분석을 기반으로 적용된 시각적 효과와 함께 미리보기를 제공합니다.
  카메라에 표시되는 화면을 보여주고 사용자 행동에 따라 사진을 찍습니다.

확장 프로그램을 사용하는 경우 ImageCapturePreview만 보장됩니다. OEM 구현에 따라 ImageAnalysis를 사용하지 못할 수도 있습니다.

ImageCapture는 단독으로 작동하지 않지만 PreviewImageAnalysis는 단독으로 작동합니다.

권한

앱에 CAMERA 권한이 있어야 합니다. Android 10 이상을 실행하는 기기가 아닌 경우 파일에 이미지를 저장하려면 WRITE_EXTERNAL_STORAGE 권한도 있어야 합니다.

앱의 권한 구성에 관해 자세히 알아보려면 앱 권한 요청을 참고하세요.

요구사항

CameraX의 최소 버전 요구사항은 다음과 같습니다.

  • Android API 수준 21
  • Android 아키텍처 구성요소 1.1.1

수명 주기 인식 활동의 경우 FragmentActivity 또는 AppCompatActivity를 사용합니다.

종속 항목 선언

CameraX에 종속 항목을 추가하려면 프로젝트에 Google Maven 저장소를 추가해야 합니다.

프로젝트의 build.gradle 파일을 열고 다음과 같이 google() 저장소를 추가합니다.

Groovy

allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

Kotlin

allprojects {
    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.0.1"
  // 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 View class
  implementation "androidx.camera:camera-view:1.0.0-alpha28"
  // If you want to additionally use the CameraX Extensions library
  implementation "androidx.camera:camera-extensions:1.0.0-alpha28"
}

Kotlin

dependencies {
    // CameraX core library using the camera2 implementation
    val camerax_version = "1.0.1"
    // 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 View class
    implementation("androidx.camera:camera-view:1.0.0-alpha28")
    // If you want to additionally use the CameraX Extensions library
    implementation("androidx.camera:camera-extensions:1.0.0-alpha28")
}

이러한 요구사항을 준수하도록 앱을 구성하는 방법을 자세히 알아보려면 종속 항목 선언을 참고하세요.

추가 리소스

CameraX에 관해 자세히 알아보려면 다음 추가 리소스를 참고하세요.

Codelab

  • CameraX 시작하기
  • 코드 샘플

  • 공식 CameraX 샘플 앱