在移动应用开发领域,相机功能始终占据着重要地位。随着Android 5.0引入Camera2 API,开发者获得了更强大的相机控制能力,但同时也面临着更复杂的技术挑战。本文将深入探讨Camera2 API的全流程实现,特别针对开发中常见的崩溃、性能问题和兼容性难题提供系统化解决方案。
现代Android开发中,权限管理已从简单的清单声明演变为复杂的运行时交互系统。对于相机权限,我们需要考虑以下关键点:
xml复制<!-- AndroidManifest.xml中的基础声明 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
运行时权限请求的最佳实践:
kotlin复制private fun checkCameraPermission() {
when {
ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) ==
PackageManager.PERMISSION_GRANTED -> {
// 权限已授予
initializeCamera()
}
shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> {
// 解释权限必要性
showPermissionRationaleDialog()
}
else -> {
// 直接请求权限
requestPermissions(arrayOf(Manifest.permission.CAMERA),
REQUEST_CAMERA_PERMISSION)
}
}
}
注意:Android 10+需要额外处理后台位置权限,如果应用需要访问照片中的位置信息
CameraManager是连接应用与相机硬件的桥梁,合理使用其功能可以显著提升用户体验:
java复制val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
val cameraIds = cameraManager.cameraIdList
cameraIds.forEach { cameraId ->
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
// 检查相机方向
val lensFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
// 获取支持的输出配置
val configMap = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
// 检查支持的硬件级别
val hardwareLevel = characteristics.get(
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
}
常见硬件级别对比:
| 硬件级别 | 特性支持 | 适用场景 |
|---|---|---|
| LEGACY | 基本功能 | 兼容旧设备 |
| LIMITED | 部分高级功能 | 中端设备 |
| FULL | 完整功能集 | 主流设备 |
| LEVEL_3 | 高级功能 | 旗舰设备 |
Camera2 API的核心是建立高效的相机管道,这需要精心配置CaptureSession:
kotlin复制// 准备目标Surface列表
val surfaces = mutableListOf<Surface>().apply {
add(previewSurface) // 预览Surface
add(imageReader.surface) // 拍照Surface
if (recordVideo) {
add(mediaRecorder.surface) // 录像Surface
}
}
// 创建会话配置
val sessionConfig = SessionConfiguration(
SessionConfiguration.SESSION_REGULAR,
surfaces,
ContextCompat.getMainExecutor(this),
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
// 会话就绪处理
}
override fun onConfigureFailed(session: CameraCaptureSession) {
// 失败处理
}
}
)
// 创建会话
cameraDevice.createCaptureSession(sessionConfig)
Camera2 API提供了多种图像处理方式,ImageReader是最常用的方案之一:
java复制// 创建ImageReader
val imageReader = ImageReader.newInstance(
width, height, ImageFormat.JPEG, 3).apply {
setOnImageAvailableListener({ reader ->
val image = reader.acquireNextImage()
// 处理图像数据
processImage(image)
image.close()
}, backgroundHandler)
}
private fun processImage(image: Image) {
val buffer = image.planes[0].buffer
val bytes = ByteArray(buffer.remaining())
buffer.get(bytes)
// 异步保存图像
CoroutineScope(Dispatchers.IO).launch {
saveImageToFile(bytes)
}
}
图像方向处理的常见问题解决方案:
kotlin复制fun getImageRotation(cameraId: String): Int {
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
val sensorOrientation = characteristics.get(
CameraCharacteristics.SENSOR_ORIENTATION) ?: 0
val rotation = when (windowManager.defaultDisplay.rotation) {
Surface.ROTATION_0 -> 0
Surface.ROTATION_90 -> 90
Surface.ROTATION_180 -> 180
Surface.ROTATION_270 -> 270
else -> 0
}
return (sensorOrientation - rotation + 360) % 360
}
现代相机应用需要智能的对焦和曝光控制:
java复制// 配置自动对焦区域
val focusArea = MeteringRectangle(
/*x*/ -100, /*y*/ -100, /*width*/ 200, /*height*/ 200,
/*weight*/ MeteringRectangle.METERING_WEIGHT_MAX)
val focusRequest = CaptureRequest.Builder()
.set(CaptureRequest.CONTROL_AF_REGIONS, arrayOf(focusArea))
.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_AUTO)
.set(CaptureRequest.CONTROL_AE_REGIONS, arrayOf(focusArea))
.build()
cameraCaptureSession.capture(focusRequest, null, handler)
对于需要快速响应的场景,可以使用以下优化策略:
kotlin复制// 预加载拍照请求
val stillCaptureBuilder = cameraDevice.createCaptureRequest(
CameraDevice.TEMPLATE_STILL_CAPTURE).apply {
addTarget(imageReader.surface)
set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
}
// 设置高速连拍模式
val burstRequests = listOf(
stillCaptureBuilder.build(),
stillCaptureBuilder.build(),
stillCaptureBuilder.build()
)
cameraCaptureSession.captureBurst(burstRequests, null, handler)
Camera2开发中常见的崩溃原因及解决方案:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| CameraAccessException | 相机资源被占用 | 实现正确的生命周期管理 |
| IllegalStateException | 会话未正确配置 | 检查Surface状态 |
| NullPointerException | 回调处理不当 | 添加空值检查 |
| BufferOverflowException | 图像处理不及时 | 优化ImageReader配置 |
java复制private val cameraThread = HandlerThread("CameraBackground").apply {
start()
}
private val cameraHandler = Handler(cameraThread.looper)
override fun onDestroy() {
super.onDestroy()
cameraThread.quitSafely()
}
kotlin复制override fun onPause() {
super.onPause()
cameraSession?.close()
cameraDevice?.close()
imageReader?.close()
}
java复制// 使用YUV_420_888格式处理原始数据
val imageReader = ImageReader.newInstance(
width, height, ImageFormat.YUV_420_888, 2)
imageReader.setOnImageAvailableListener({ reader ->
val image = reader.acquireNextImage()
// 使用RenderScript或OpenCV处理YUV数据
processYuvImage(image)
image.close()
}, handler)
在实际项目中,我们发现正确处理相机生命周期和及时释放资源可以避免90%以上的稳定性问题。特别是在多Fragment场景下,需要特别注意相机资源的释放和重新获取时机。