Android CameraX 2026: Hướng Dẫn Chụp Ảnh và Quay Video với Câu Hỏi Phỏng Vấn
Tìm hiểu CameraX trên Android 2026: chụp ảnh, quay video, tích hợp Jetpack Compose và các câu hỏi phỏng vấn kỹ thuật dành cho lập trình viên Android.

Android CameraX đơn giản hóa việc phát triển camera bằng cách trừu tượng hóa sự phức tạp của phần cứng thông qua API nhận biết vòng đời (lifecycle-aware). Với phiên bản 1.6.2 đã ổn định và việc di chuyển sang CameraPipe hoàn tất, CameraX mang đến stack camera hiệu suất cao giống như ứng dụng camera Pixel.
CameraX 1.6 đã di chuyển sang CameraPipe, sử dụng Media3 Muxer mặc định cho VideoCapture và ổn định hóa SessionConfig API. Kotlin DSL builders (preview { }, imageCapture { }, videoCapture { }) giảm boilerplate đến 40%.
Kiến Trúc CameraX và Mô Hình Use Case
CameraX cung cấp bốn use case chính có thể chạy đồng thời: Preview, ImageCapture, ImageAnalysis và VideoCapture. Mỗi use case được liên kết với LifecycleOwner, vì vậy camera tự động khởi động và dừng theo activity hoặc fragment.
CameraProvider đóng vai trò factory tạo instance các use case và liên kết chúng với camera. Sự tách biệt này đảm bảo cùng một mã nguồn chạy trên các thiết bị từ nhiều OEM khác nhau mà không cần lo lắng về đặc thù của từng nhà sản xuất.
class CameraSetup(
private val context: Context,
private val lifecycleOwner: LifecycleOwner
) {
private lateinit var cameraProvider: ProcessCameraProvider
private var imageCapture: ImageCapture? = null
private var videoCapture: VideoCapture<Recorder>? = null
suspend fun initialize() {
// ProcessCameraProvider binds use cases to lifecycle
cameraProvider = ProcessCameraProvider.getInstance(context).await()
}
fun bindUseCases(previewView: PreviewView) {
// Unbind existing use cases before rebinding
cameraProvider.unbindAll()
// Preview use case displays camera feed
val preview = Preview.Builder()
.build()
.apply { setSurfaceProvider(previewView.surfaceProvider) }
// ImageCapture for photos
imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
// VideoCapture with Recorder
val recorder = Recorder.Builder()
.setQualitySelector(QualitySelector.from(Quality.FHD))
.build()
videoCapture = VideoCapture.withOutput(recorder)
// Bind all use cases to lifecycle
cameraProvider.bindToLifecycle(
lifecycleOwner,
CameraSelector.DEFAULT_BACK_CAMERA,
preview,
imageCapture,
videoCapture
)
}
}Lời gọi bindToLifecycle kết nối use case với cả phần cứng camera và lifecycle. Khi LifecycleOwner chuyển sang trạng thái STARTED, camera sẽ mở. Khi chuyển sang STOPPED, camera tự động đóng.
Triển Khai Chụp Ảnh với ImageCapture
ImageCapture cung cấp hai chế độ chụp: CAPTURE_MODE_MINIMIZE_LATENCY cho phản hồi nhanh khi bấm chụp, và CAPTURE_MODE_MAXIMIZE_QUALITY cho ảnh chụp với xử lý chuyên sâu. Sự lựa chọn phụ thuộc vào việc ứng dụng ưu tiên tốc độ (ứng dụng mạng xã hội) hay chất lượng (máy quét tài liệu).
class PhotoCaptureManager(
private val context: Context,
private val imageCapture: ImageCapture
) {
private val mainExecutor = ContextCompat.getMainExecutor(context)
fun capturePhoto(onResult: (Uri?) -> Unit) {
// Generate unique filename with timestamp
val filename = "IMG_\${System.currentTimeMillis()}.jpg"
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
// Store in Pictures/CameraX folder on Android 10+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX")
}
}
val outputOptions = ImageCapture.OutputFileOptions.Builder(
context.contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
).build()
imageCapture.takePicture(
outputOptions,
mainExecutor,
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(results: ImageCapture.OutputFileResults) {
// results.savedUri contains the MediaStore URI
onResult(results.savedUri)
}
override fun onError(exception: ImageCaptureException) {
Log.e("PhotoCapture", "Capture failed: \${exception.message}")
onResult(null)
}
}
)
}
}CameraX 1.5 đã thêm hỗ trợ chụp DNG (RAW) cho các ứng dụng cần dữ liệu cảm biến thô. Kiểm tra khả năng thiết bị với ImageCapture.getImageCaptureCapabilities() trước khi bật đầu ra RAW.
Quay Video với VideoCapture và Recorder
VideoCapture sử dụng đối tượng Recorder để xử lý encoding và muxing. Recorder API tuân theo mẫu prepare-start-stop, tự động xử lý đồng bộ âm thanh.
class VideoRecordingManager(
private val context: Context,
private val videoCapture: VideoCapture<Recorder>
) {
private var activeRecording: Recording? = null
fun startRecording(onEvent: (VideoRecordEvent) -> Unit): Boolean {
// Check if already recording
if (activeRecording != null) return false
val filename = "VID_\${System.currentTimeMillis()}.mp4"
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/CameraX")
}
}
val mediaStoreOutput = MediaStoreOutputOptions.Builder(
context.contentResolver,
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
)
.setContentValues(contentValues)
.build()
// Prepare recording with audio if permission granted
val pendingRecording = videoCapture.output
.prepareRecording(context, mediaStoreOutput)
if (hasAudioPermission()) {
pendingRecording.withAudioEnabled()
}
// Start recording and store reference
activeRecording = pendingRecording.start(
ContextCompat.getMainExecutor(context)
) { event ->
onEvent(event)
// Clear reference when recording completes
if (event is VideoRecordEvent.Finalize) {
activeRecording = null
}
}
return true
}
fun stopRecording() {
activeRecording?.stop()
}
fun pauseRecording() {
activeRecording?.pause()
}
fun resumeRecording() {
activeRecording?.resume()
}
private fun hasAudioPermission(): Boolean {
return ContextCompat.checkSelfPermission(
context,
Manifest.permission.RECORD_AUDIO
) == PackageManager.PERMISSION_GRANTED
}
}CameraX 1.5 đã thêm hỗ trợ quay video slow-motion. Truy vấn khả năng với Recorder.getHighSpeedVideoCapabilities() trước khi bật quay 120fps hoặc 240fps.
Sẵn sàng chinh phục phỏng vấn Android?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
CameraX với Jetpack Compose
CameraX 1.5 giới thiệu artifact camera-compose với composable CameraXViewfinder. Đối với các ứng dụng vẫn sử dụng PreviewView dựa trên View, AndroidView cung cấp khả năng tương tác.
@Composable
fun CameraPreview(
modifier: Modifier = Modifier,
onPreviewViewCreated: (PreviewView) -> Unit
) {
AndroidView(
modifier = modifier.fillMaxSize(),
factory = { context ->
PreviewView(context).apply {
implementationMode = PreviewView.ImplementationMode.COMPATIBLE
scaleType = PreviewView.ScaleType.FILL_CENTER
onPreviewViewCreated(this)
}
}
)
}
@Composable
fun CameraScreen(viewModel: CameraViewModel = viewModel()) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(Unit) {
viewModel.initializeCamera(context, lifecycleOwner)
}
Box(modifier = Modifier.fillMaxSize()) {
CameraPreview(
onPreviewViewCreated = { previewView ->
viewModel.bindPreview(previewView)
}
)
// Capture buttons overlay
Row(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(32.dp),
horizontalArrangement = Arrangement.spacedBy(24.dp)
) {
IconButton(
onClick = { viewModel.capturePhoto() }
) {
Icon(Icons.Default.Camera, "Take photo")
}
IconButton(
onClick = { viewModel.toggleRecording() }
) {
Icon(
if (viewModel.isRecording) Icons.Default.Stop
else Icons.Default.Videocam,
"Record video"
)
}
}
}
}Đối với các triển khai ưu tiên Compose, hướng dẫn câu hỏi phỏng vấn Jetpack Compose bao gồm các mẫu tích hợp ViewModel có thể áp dụng trực tiếp cho quản lý trạng thái camera.
Xử Lý Quyền Camera
Android yêu cầu quyền CAMERA khi chạy. RECORD_AUDIO chỉ cần thiết khi quay video có âm thanh. Luồng xử lý quyền nên giải thích lý do cần truy cập trước khi yêu cầu.
class CameraPermissionHandler(private val activity: ComponentActivity) {
private val requiredPermissions = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
)
private val permissionLauncher = activity.registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val cameraGranted = permissions[Manifest.permission.CAMERA] == true
val audioGranted = permissions[Manifest.permission.RECORD_AUDIO] == true
if (cameraGranted) {
onCameraPermissionGranted(audioGranted)
} else {
onCameraPermissionDenied()
}
}
fun checkAndRequestPermissions() {
when {
hasAllPermissions() -> onCameraPermissionGranted(hasAudioPermission())
shouldShowRationale() -> showPermissionRationale()
else -> permissionLauncher.launch(requiredPermissions)
}
}
private fun hasAllPermissions(): Boolean {
return requiredPermissions.all { permission ->
ContextCompat.checkSelfPermission(
activity, permission
) == PackageManager.PERMISSION_GRANTED
}
}
private fun hasAudioPermission(): Boolean {
return ContextCompat.checkSelfPermission(
activity,
Manifest.permission.RECORD_AUDIO
) == PackageManager.PERMISSION_GRANTED
}
private fun shouldShowRationale(): Boolean {
return requiredPermissions.any { permission ->
ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)
}
}
}Câu Hỏi Phỏng Vấn CameraX
CameraX thường xuất hiện trong các cuộc phỏng vấn Android khi thảo luận về tích hợp camera, quản lý lifecycle hoặc trừu tượng hóa phần cứng. Những câu hỏi này kiểm tra hiểu biết về kiến trúc và chi tiết triển khai thực tế.
Tại sao sử dụng CameraX thay vì Camera2?
CameraX trừu tượng hóa các đặc thù thiết bị mà Camera2 để lộ trực tiếp. Triển khai Camera2 đòi hỏi xử lý hàng trăm workaround dành riêng cho từng thiết bị. CameraX tích hợp sẵn các bản sửa lỗi này vào thư viện, được kiểm tra trên CameraX Test Lab bao gồm hơn 150 mẫu thiết bị.
Binding nhận biết lifecycle loại bỏ nhu cầu quản lý tài nguyên thủ công. Camera2 yêu cầu các lệnh gọi open/close rõ ràng có thể gây rò rỉ bộ nhớ nếu không xử lý đúng trong mọi callback lifecycle.
CameraProvider liên kết use case với lifecycle như thế nào?
CameraProvider sử dụng mẫu LifecycleObserver bên trong. Khi gọi bindToLifecycle(), nó đăng ký observer trên LifecycleOwner được cung cấp. Observer này nhận các sự kiện ON_START và ON_STOP để mở và đóng camera.
Điểm quan trọng: CameraX không khởi động camera ngay khi binding. Nó chờ lifecycle đạt trạng thái STARTED, tương ứng với thời điểm Activity.onStart().
Điều gì xảy ra khi liên kết các use case không tương thích?
Không phải tất cả tổ hợp use case đều hoạt động trên mọi thiết bị. Preview + VideoCapture + ImageAnalysis + ImageCapture có thể thất bại trên phần cứng cũ. CameraX ném IllegalArgumentException tại thời điểm binding, không phải runtime.
Giải pháp là kiểm tra CameraProvider.hasCamera() và khả năng thiết bị trước khi liên kết các tổ hợp phức tạp. Đối với ứng dụng quan trọng, cần kiểm tra rõ ràng trên các thiết bị cấu hình thấp.
Giải thích sự khác biệt giữa CAPTURE_MODE_MINIMIZE_LATENCY và CAPTURE_MODE_MAXIMIZE_QUALITY
MINIMIZE_LATENCY giảm độ trễ chụp bằng cách sử dụng pipeline xử lý nhanh hơn, có thể bỏ qua một số hậu xử lý. MAXIMIZE_QUALITY áp dụng xử lý HDR+ đầy đủ, giảm nhiễu và chụp đa khung hình trên các thiết bị được hỗ trợ.
Đánh đổi: chế độ latency chụp trong 100-200ms, chế độ quality có thể mất 500ms-1s tùy thuộc ánh sáng. Ứng dụng mạng xã hội thường chọn latency. Ứng dụng quét tài liệu chọn quality.
CameraX xử lý xoay thiết bị như thế nào?
CameraX tự động xử lý xoay thông qua cài đặt target rotation. Mặc định, nó sử dụng rotation của Display, nhưng ứng dụng có thể ghi đè bằng setTargetRotation(). Dữ liệu EXIF của ảnh đầu ra chứa tag orientation chính xác.
Đối với PreviewView, scaleType xử lý sự khác biệt tỷ lệ khung hình. FILL_CENTER cắt để lấp đầy, FIT_CENTER hiển thị letterboxing. Lựa chọn phụ thuộc vào việc ứng dụng ưu tiên preview toàn màn hình hay tránh cắt xén.
Dependencies và Cài Đặt cho CameraX 1.6
Thư viện CameraX được chia thành nhiều artifact. Chỉ bao gồm những gì ứng dụng sử dụng để giảm thiểu kích thước APK.
dependencies {
val cameraxVersion = "1.6.2"
// Core library required for all use cases
implementation("androidx.camera:camera-core:\$cameraxVersion")
// Camera2 implementation (required)
implementation("androidx.camera:camera-camera2:\$cameraxVersion")
// Lifecycle integration
implementation("androidx.camera:camera-lifecycle:\$cameraxVersion")
// VideoCapture use case
implementation("androidx.camera:camera-video:\$cameraxVersion")
// PreviewView and camera UI components
implementation("androidx.camera:camera-view:\$cameraxVersion")
// Optional: Extensions (Night mode, HDR, etc.)
implementation("androidx.camera:camera-extensions:\$cameraxVersion")
// Optional: Real-time effects
implementation("androidx.camera:camera-effects:\$cameraxVersion")
}
android {
// CameraX requires Java 11 bytecode
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}Manifest cần khai báo tính năng và quyền:
<!-- AndroidManifest.xml -->
<manifest>
<!-- Camera hardware requirement -->
<uses-feature android:name="android.hardware.camera.any" />
<!-- Runtime permissions -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest>Các mẫu liên quan cho Kotlin Coroutines áp dụng khi bọc các lệnh gọi ListenableFuture của CameraX trong các hàm suspend.
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Những Thay Đổi CameraX 1.6 cho Ứng Dụng Production
CameraX 1.6 mang đến ba thay đổi ảnh hưởng đến các triển khai hiện có:
-
Backend CameraPipe: Stack camera nội bộ giờ sử dụng CameraPipe, kiến trúc giống với camera Pixel. Điều này cải thiện hiệu suất trên thiết bị Pixel và cung cấp nền tảng sạch hơn cho các tính năng nâng cao. Mã nguồn hiện tại không cần thay đổi.
-
Media3 Muxer mặc định: VideoCapture giờ sử dụng Media3 Muxer thay vì MediaMuxer. Điều này cải thiện khả năng chống crash: nếu ứng dụng dừng trong khi quay, file video vẫn có thể phát được. Bề mặt API không thay đổi.
-
SessionConfig API ổn định: Ứng dụng giờ có thể cấu hình cài đặt camera nâng cao thông qua SessionConfig mà không cần annotation experimental. Điều này cho phép các tính năng như sử dụng đồng thời camera trước/sau trên các thiết bị được hỗ trợ.
Đường dẫn di chuyển: Cập nhật phiên bản dependency. Kiểm tra trên các thiết bị trước đây gặp vấn đề. Ghi chú phát hành CameraX ghi lại các thay đổi hành vi giữa các phiên bản.
- CameraX sử dụng binding nhận biết lifecycle:
bindToLifecycle()kết nối use case với camera và lifecycle Activity - Chế độ ImageCapture đánh đổi latency với quality: chọn dựa trên use case, không phải khả năng thiết bị
- VideoCapture yêu cầu thiết lập Recorder trước khi binding: chuẩn bị output options trước khi bắt đầu
- Xử lý quyền nên giải thích mục đích trước khi yêu cầu: giảm tỷ lệ từ chối
- CameraX 1.6 di chuyển sang CameraPipe nội bộ: không cần thay đổi API, nhưng kiểm tra trên thiết bị mục tiêu
- Câu hỏi phỏng vấn tập trung vào quản lý lifecycle, tổ hợp use case và so sánh với Camera2
Bạn có tìm ra lỗi trong Android không?
Một đoạn mã thật, một lỗi ẩn, mỗi ngày một lượt. Không cần tài khoản để thử.

Viết bởi
Anthony Fillion-MailletNgười sáng lập SharpSkill
Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.
Cập nhật ngày 11 tháng 9, 2026
Chia sẻ
Bài viết liên quan

Modularization Android 2026: Kiến Trúc Multi-Module và Câu Hỏi Phỏng Vấn
Làm chủ kiến trúc multi-module Android với convention plugins, Gradle version catalogs và feature modules. Bao gồm các câu hỏi phỏng vấn phổ biến về chiến lược modularization.

Jetpack Navigation Compose 2026: Điều Hướng Type-Safe và Câu Hỏi Phỏng Vấn
Tìm hiểu Jetpack Navigation Compose 2.10 với điều hướng type-safe sử dụng Kotlin Serialization. Hướng dẫn đầy đủ với ví dụ code và câu hỏi phỏng vấn Android.

Android WorkManager 2026: Hướng Dẫn Toàn Diện Background Tasks, Constraints và Câu Hỏi Phỏng Vấn
Tìm hiểu Android WorkManager để chạy background tasks với constraints, chaining và CoroutineWorker. Bao gồm các câu hỏi phỏng vấn Android developer thường gặp.