Android CameraX 2026: Panduan Lengkap Pengambilan Foto dan Video dengan Pertanyaan Wawancara

Pelajari CameraX di Android 2026: pengambilan foto, perekaman video, integrasi Jetpack Compose, dan pertanyaan wawancara teknis untuk developer Android.

Android CameraX 2026: Panduan Lengkap Pengambilan Foto dan Video dengan Pertanyaan Wawancara

Android CameraX menyederhanakan pengembangan kamera dengan mengabstraksi kompleksitas hardware melalui API yang lifecycle-aware. Dengan versi 1.6.2 yang kini stabil dan migrasi ke CameraPipe yang telah selesai, CameraX menghadirkan stack kamera berperforma tinggi yang sama dengan yang digunakan pada aplikasi kamera Pixel.

Perubahan Utama CameraX 1.6

CameraX 1.6 bermigrasi ke CameraPipe, menggunakan Media3 Muxer secara default untuk VideoCapture, dan menstabilkan SessionConfig API. Kotlin DSL builders (preview { }, imageCapture { }, videoCapture { }) mengurangi boilerplate hingga 40%.

Arsitektur CameraX dan Model Use Case

CameraX menyediakan empat use case utama yang dapat berjalan secara bersamaan: Preview, ImageCapture, ImageAnalysis, dan VideoCapture. Setiap use case terhubung ke LifecycleOwner, sehingga kamera secara otomatis mulai dan berhenti mengikuti activity atau fragment.

CameraProvider bertindak sebagai factory yang membuat instance use case dan mengikatnya ke kamera. Pemisahan ini memastikan kode yang sama berjalan di perangkat dari berbagai OEM tanpa perlu khawatir tentang keunikan vendor tertentu.

CameraSetup.ktkotlin
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
        )
    }
}

Pemanggilan bindToLifecycle menghubungkan use case ke hardware kamera dan lifecycle sekaligus. Ketika LifecycleOwner memasuki state STARTED, kamera terbuka. Ketika memasuki STOPPED, kamera menutup secara otomatis.

Implementasi Pengambilan Foto dengan ImageCapture

ImageCapture menyediakan dua capture mode: CAPTURE_MODE_MINIMIZE_LATENCY untuk respons shutter yang cepat, dan CAPTURE_MODE_MAXIMIZE_QUALITY untuk pengambilan dengan pemrosesan intensif. Pilihan tergantung pada apakah aplikasi mengutamakan kecepatan (aplikasi sosial) atau kualitas (pemindai dokumen).

PhotoCaptureManager.ktkotlin
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 menambahkan dukungan capture DNG (RAW) untuk aplikasi yang membutuhkan data sensor mentah. Periksa kemampuan perangkat dengan ImageCapture.getImageCaptureCapabilities() sebelum mengaktifkan output RAW.

Merekam Video dengan VideoCapture dan Recorder

VideoCapture menggunakan objek Recorder untuk menangani encoding dan muxing. Recorder API mengikuti pola prepare-start-stop yang menangani sinkronisasi audio secara otomatis.

VideoRecordingManager.ktkotlin
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 menambahkan dukungan video slow-motion. Query kemampuan dengan Recorder.getHighSpeedVideoCapabilities() sebelum mengaktifkan perekaman 120fps atau 240fps.

Siap menguasai wawancara Android Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

CameraX dengan Jetpack Compose

CameraX 1.5 memperkenalkan artefak camera-compose dengan composable CameraXViewfinder. Untuk aplikasi yang masih menggunakan PreviewView berbasis View, AndroidView menyediakan interoperabilitas.

CameraPreviewComposable.ktkotlin
@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"
                )
            }
        }
    }
}

Untuk implementasi yang mengutamakan Compose, panduan pertanyaan wawancara Jetpack Compose mencakup pola integrasi ViewModel yang dapat diterapkan langsung pada manajemen state kamera.

Menangani Izin Kamera

Android memerlukan izin CAMERA saat runtime. RECORD_AUDIO hanya diperlukan saat merekam video dengan suara. Alur izin harus menjelaskan mengapa akses diperlukan sebelum meminta.

PermissionHandler.ktkotlin
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)
        }
    }
}

Pertanyaan Wawancara CameraX

CameraX sering muncul dalam wawancara Android saat membahas integrasi kamera, manajemen lifecycle, atau abstraksi hardware. Pertanyaan-pertanyaan ini menguji pemahaman tentang arsitektur dan detail implementasi praktis.

Mengapa menggunakan CameraX daripada Camera2?

CameraX mengabstraksi keunikan perangkat yang langsung diekspos oleh Camera2. Implementasi Camera2 memerlukan penanganan ratusan workaround khusus perangkat. CameraX menyematkan perbaikan ini ke dalam library, yang diuji terhadap CameraX Test Lab yang mencakup lebih dari 150 model perangkat.

Binding yang lifecycle-aware menghilangkan kebutuhan manajemen resource secara manual. Camera2 memerlukan panggilan open/close eksplisit yang menyebabkan kebocoran memori jika tidak ditangani dengan benar di setiap callback lifecycle.

Bagaimana CameraProvider mengikat use case ke lifecycle?

CameraProvider menggunakan pola LifecycleObserver secara internal. Saat memanggil bindToLifecycle(), ia mendaftarkan observer pada LifecycleOwner yang diberikan. Observer ini menerima event ON_START dan ON_STOP untuk membuka dan menutup kamera.

Insight utama: CameraX tidak langsung memulai kamera saat binding. Ia menunggu lifecycle mencapai state STARTED, yang sesuai dengan waktu Activity.onStart().

Apa yang terjadi saat mengikat use case yang tidak kompatibel?

Tidak semua kombinasi use case berfungsi di semua perangkat. Preview + VideoCapture + ImageAnalysis + ImageCapture mungkin gagal pada hardware lama. CameraX melempar IllegalArgumentException saat binding, bukan saat runtime.

Solusinya adalah memeriksa CameraProvider.hasCamera() dan kemampuan perangkat sebelum mengikat kombinasi kompleks. Untuk aplikasi kritis, uji secara eksplisit pada perangkat low-end.

Jelaskan perbedaan antara CAPTURE_MODE_MINIMIZE_LATENCY dan CAPTURE_MODE_MAXIMIZE_QUALITY

MINIMIZE_LATENCY mengurangi lag shutter dengan menggunakan pipeline pemrosesan yang lebih cepat, mungkin melewatkan beberapa post-processing. MAXIMIZE_QUALITY menerapkan pemrosesan HDR+ penuh, pengurangan noise, dan multi-frame capture pada perangkat yang didukung.

Tradeoff-nya: mode latency mengambil gambar dalam 100-200ms, mode quality mungkin membutuhkan 500ms-1s tergantung pencahayaan. Aplikasi sosial biasanya memilih latency. Aplikasi pemindai dokumen memilih quality.

Bagaimana CameraX menangani rotasi perangkat?

CameraX secara otomatis menangani rotasi melalui pengaturan target rotation. Secara default, ia menggunakan rotasi Display, tetapi aplikasi dapat menimpa dengan setTargetRotation(). Data EXIF gambar output berisi tag orientasi yang benar.

Untuk PreviewView, scaleType menangani perbedaan aspek rasio. FILL_CENTER memotong untuk mengisi, FIT_CENTER menampilkan letterboxing. Pilihan tergantung pada apakah aplikasi mengutamakan preview layar penuh atau menghindari pemotongan.

Dependensi dan Setup untuk CameraX 1.6

Library CameraX dibagi menjadi beberapa artefak. Sertakan hanya yang digunakan aplikasi untuk meminimalkan ukuran APK.

build.gradle.kts (Module: app)kotlin
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 memerlukan deklarasi fitur dan izin:

xml
<!-- 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>

Pola terkait untuk Kotlin Coroutines berlaku saat membungkus panggilan ListenableFuture CameraX dalam fungsi suspend.

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Perubahan CameraX 1.6 untuk Aplikasi Produksi

CameraX 1.6 membawa tiga perubahan yang mempengaruhi implementasi yang ada:

  1. Backend CameraPipe: Stack kamera internal sekarang menggunakan CameraPipe, arsitektur yang sama dengan kamera Pixel. Ini meningkatkan performa pada perangkat Pixel dan menyediakan fondasi yang lebih bersih untuk fitur lanjutan. Kode yang ada tidak perlu perubahan.

  2. Media3 Muxer default: VideoCapture sekarang menggunakan Media3 Muxer sebagai pengganti MediaMuxer. Ini meningkatkan ketahanan terhadap crash: jika aplikasi berhenti selama perekaman, file video tetap dapat diputar. Permukaan API tidak berubah.

  3. SessionConfig API stabil: Aplikasi sekarang dapat mengkonfigurasi pengaturan kamera lanjutan melalui SessionConfig tanpa anotasi experimental. Ini memungkinkan fitur seperti penggunaan kamera depan/belakang secara simultan pada perangkat yang didukung.

Jalur migrasi: Perbarui versi dependensi. Uji pada perangkat yang sebelumnya memiliki masalah. Catatan rilis CameraX mendokumentasikan perubahan perilaku antar versi.

  • CameraX menggunakan binding lifecycle-aware: bindToLifecycle() menghubungkan use case ke kamera dan lifecycle Activity
  • Mode ImageCapture menukar latency dengan kualitas: pilih berdasarkan use case, bukan kemampuan perangkat
  • VideoCapture memerlukan setup Recorder sebelum binding: siapkan opsi output sebelum memulai
  • Penanganan izin harus menjelaskan tujuan sebelum meminta: mengurangi tingkat penolakan
  • CameraX 1.6 bermigrasi ke CameraPipe secara internal: tidak ada perubahan API yang diperlukan, tetapi uji pada perangkat target
  • Pertanyaan wawancara fokus pada manajemen lifecycle, kombinasi use case, dan perbandingan dengan Camera2
Tantangan harian

Bisakah kamu menemukan bug di Android?

Satu potongan kode nyata, satu bug tersembunyi, satu percobaan per hari. Tanpa akun untuk mencoba.

Anthony Fillion-Maillet

Ditulis oleh

Anthony Fillion-Maillet

Pendiri SharpSkill

Developer fullstack selama lebih dari 10 tahun. Ia menjalankan SharpSkill dan bertanggung jawab atas semua yang diterbitkan di sini.

Diperbarui 11 September 2026

Bagikan

Artikel terkait