이 페이지에서는 CameraX의 구조, API의 작동 방식, 수명 주기 활용 방법, 사용 사례를 결합하는 방법을 포함하여 CameraX의 아키텍처를 다룹니다.
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())
자바
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)); }
다음 구성 조합에 관한 지원이 보장됩니다(미리보기 또는 동영상 캡처가 필요하지만, 동시에 둘 다 필요하지는 않은 경우).
미리보기 또는 동영상 캡처 | 이미지 캡처 | 분석 | 설명 |
---|---|---|---|
사용자에게 미리보기를 제공하거나 동영상 녹화 및 사진 촬영, 이미지 스트림 분석 | |||
사진 촬영, 이미지 스트림 분석 | |||
사용자에게 미리보기를 제공하거나 동영상 녹화 및 사진 촬영 | |||
사용자에게 미리보기를 제공하거나 동영상 녹화 및 이미지 스트림 분석 |
미리보기와 동영상 캡처가 모두 필요한 경우 다음과 같은 사용 사례 조합이 조건부로 지원됩니다.
미리보기 | 동영상 캡처 | 이미지 캡처 | 분석 | 특별 요구사항 |
---|---|---|---|---|
모든 카메라에서 보장 | ||||
제한된(또는 그 이상) 카메라 기기 | ||||
LEVEL_3(또는 그 이상) 카메라 기기 |
또한 다음 사항에 유의하세요.
- 모든 사용 사례가 단독으로 작동할 수 있습니다. 예를 들어, 앱은 미리보기를 사용하지 않고 동영상을 녹화할 수 있습니다.
- 확장 프로그램을 사용하는 경우
ImageCapture
및Preview
조합만 작동이 보장됩니다. OEM 구현에 따라ImageAnalysis
를 추가하지 못할 수도 있습니다.VideoCapture
사용 사례에 확장 프로그램을 사용할 수 없습니다. 자세한 내용은 확장 프로그램 참조 문서를 확인하세요. - 카메라 기능에 따라 일부 카메라는 낮은 해상도 모드에서 조합을 지원할 수 있지만, 일부 높은 해상도에서는 동일한 조합을 지원할 수 없습니다.
지원되는 하드웨어 수준은 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 }
자바
@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
권한이 있어야 합니다. Android 10 이상을 실행하는 기기가 아닌 경우 파일에 이미지를 저장하려면 WRITE_EXTERNAL_STORAGE
권한도 있어야 합니다.
앱의 권한 구성에 관해 자세히 알아보려면 앱 권한 요청을 참고하세요.
요구사항
CameraX의 최소 버전 요구사항은 다음과 같습니다.
- Android API 수준 21
- Android 아키텍처 구성요소 1.1.1
수명 주기 인식 활동의 경우 FragmentActivity
또는 AppCompatActivity
를 사용합니다.
종속 항목 선언
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.3.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.3.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}") }
이러한 요구사항을 준수하도록 앱을 구성하는 방법을 자세히 알아보려면 종속 항목 선언을 참고하세요.
추가 리소스
CameraX에 관해 자세히 알아보려면 다음 추가 리소스를 참고하세요.
Codelab
코드 샘플