如何将旧版CameraX拍照代码升级至1.3.0稳定版?
CameraX 从1.0.0 alpha05 升级到1.3.0稳定版完整指南及代码示例
一、依赖配置升级
替换旧版CameraX依赖,使用1.3.0稳定版组件:
// 模块级 build.gradle dependencies { // CameraX 核心生命周期管理 implementation "androidx.camera:camera-lifecycle:1.3.0" // Camera2 扩展适配 implementation "androidx.camera:camera-camera2:1.3.0" // 预览用例 implementation "androidx.camera:camera-preview:1.3.0" // 拍照用例 implementation "androidx.camera:camera-core:1.3.0" // 可选:简化预览布局的View组件 implementation "androidx.camera:camera-view:1.3.0" }
二、核心API变更说明
- 移除
CameraX.unbindAll()/CameraX.bindToLifecycle(),改用ProcessCameraProvider统一管理相机生命周期绑定 - 相机用例(预览、拍照)通过标准化Builder模式创建
- 摄像头切换通过修改
CameraSelector后重新绑定实现,无需手动解绑再绑定
三、完整代码示例(Kotlin)
1. Activity代码
import android.Manifest import android.content.pm.PackageManager import android.os.Bundle import android.widget.Button import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.camera.core.* import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.view.PreviewView import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import java.io.File import java.util.concurrent.ExecutorService import java.util.concurrent.Executors class CameraXActivity : AppCompatActivity() { private lateinit var previewView: PreviewView private lateinit var captureBtn: Button private lateinit var switchCameraBtn: Button private var imageCapture: ImageCapture? = null private var cameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA private lateinit var cameraExecutor: ExecutorService override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) previewView = findViewById(R.id.preview_view) captureBtn = findViewById(R.id.capture_btn) switchCameraBtn = findViewById(R.id.switch_camera_btn) cameraExecutor = Executors.newSingleThreadExecutor() // 权限校验 if (allPermissionsGranted()) { startCamera() } else { ActivityCompat.requestPermissions( this, REQUIRED_PERMISSIONS, REQUEST_PERM_CODE ) } // 拍照逻辑 captureBtn.setOnClickListener { takePhoto() } // 摄像头切换逻辑 switchCameraBtn.setOnClickListener { cameraSelector = if (cameraSelector == CameraSelector.DEFAULT_BACK_CAMERA) { CameraSelector.DEFAULT_FRONT_CAMERA } else { CameraSelector.DEFAULT_BACK_CAMERA } startCamera() } } private fun startCamera() { val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener({ val cameraProvider = cameraProviderFuture.get() // 构建预览用例 val preview = Preview.Builder() .build() .also { it.setSurfaceProvider(previewView.surfaceProvider) } // 构建拍照用例 imageCapture = ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build() try { // 解绑所有已绑定的用例 cameraProvider.unbindAll() // 绑定用例到Activity生命周期 cameraProvider.bindToLifecycle( this, cameraSelector, preview, imageCapture ) } catch (e: Exception) { Toast.makeText(this, "相机启动失败: ${e.message}", Toast.LENGTH_SHORT).show() } }, ContextCompat.getMainExecutor(this)) } private fun takePhoto() { val imageCapture = imageCapture ?: return // 生成照片存储路径 val photoFile = File( externalMediaDirs.firstOrNull(), "${System.currentTimeMillis()}.jpg" ) val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build() imageCapture.takePicture( outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback { override fun onImageSaved(output: ImageCapture.OutputFileResults) { Toast.makeText(this@CameraXActivity, "照片已保存: ${photoFile.path}", Toast.LENGTH_SHORT).show() } override fun onError(e: ImageCaptureException) { Toast.makeText(this@CameraXActivity, "拍照失败: ${e.message}", Toast.LENGTH_SHORT).show() } } ) } private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all { ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == REQUEST_PERM_CODE) { if (allPermissionsGranted()) { startCamera() } else { Toast.makeText(this, "权限被拒绝,无法使用相机", Toast.LENGTH_SHORT).show() finish() } } } override fun onDestroy() { super.onDestroy() cameraExecutor.shutdown() } companion object { private const val REQUEST_PERM_CODE = 100 private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA) } }
2. 布局文件(activity_camera.xml)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <androidx.camera.view.PreviewView android:id="@+id/preview_view" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="16dp" android:gravity="center" android:orientation="horizontal"> <Button android:id="@+id/switch_camera_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="切换摄像头" /> <Button android:id="@+id/capture_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="拍照" android:layout_marginStart="32dp" /> </LinearLayout> </LinearLayout>
四、关键注意事项
- Android 11适配:保存到外部媒体目录无需额外存储权限,若需自定义目录需申请
MANAGE_EXTERNAL_STORAGE权限 - 生命周期绑定:
ProcessCameraProvider.bindToLifecycle()会自动跟随Activity生命周期,页面销毁时无需手动解绑 - 异常处理:必须捕获相机绑定、拍照过程中的异常,避免因硬件兼容性问题(如部分Oppo设备)导致崩溃
- 线程安全:拍照操作需在后台线程执行,示例中使用
ExecutorService确保线程安全
内容的提问来源于stack exchange,提问作者anta40
相关产品推荐
相关产品推荐

