如何高效掌握Android Camera2 API开发?从基础到实战的进阶指南
Android相机应用开发一直是移动开发领域的重要方向,而Camera2 API作为Android 5.0引入的现代相机框架,为开发者提供了前所未有的控制能力。本文将系统解析android-Camera2Basic开源项目,帮助开发者从概念理解到实际应用,全面掌握Camera2 API的核心技术与最佳实践。
一、概念解析:理解Camera2 API的技术架构
1.1 Camera2 API与传统相机框架的差异
Camera2 API采用了全新的架构设计,与传统Camera API相比,其最大区别在于引入了"管道"(Pipeline)概念。如果将传统Camera API比作傻瓜相机(自动模式为主,手动控制有限),那么Camera2 API则更像专业单反相机,提供从对焦、曝光到白平衡的全手动控制能力。
这种架构转变带来了三个核心优势:
- 支持高帧率图像捕获
- 提供逐帧参数控制
- 允许同时处理多个数据流
1.2 Camera2 API核心组件解析
Camera2 API基于以下关键组件构建:
相机设备(CameraDevice):代表物理相机设备,负责图像数据的捕获。可以将其理解为相机的"机身",是所有操作的基础。
捕获会话(CameraCaptureSession):管理相机预览和拍照请求的执行。相当于相机的"拍摄模式",决定了如何处理捕获的图像数据。
捕获请求(CaptureRequest):包含单次拍摄的参数设置。类似于相机的"参数调节旋钮",控制曝光、对焦等具体参数。
图像 reader(ImageReader):用于获取捕获的图像数据。相当于相机的"存储卡",存储和处理拍摄的照片。
1.3 项目核心文件功能定位
android-Camera2Basic项目通过以下关键文件实现了Camera2 API的基础应用:
kotlinApp/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.kt:相机功能的核心实现,管理相机生命周期和用户交互kotlinApp/Application/src/main/java/com/example/android/camera2basic/AutoFitTextureView.kt:实现自适应预览界面,确保不同设备上的显示效果kotlinApp/Application/src/main/java/com/example/android/camera2basic/ImageSaver.kt:处理图像数据保存逻辑,负责将原始图像转换为JPEG格式
二、核心价值:项目的技术亮点与学习意义
2.1 完整的相机生命周期管理
项目展示了如何正确处理相机资源的创建、使用和释放过程。在移动开发中,相机资源是一种稀缺资源,不正确的管理可能导致应用崩溃或设备过热。
Camera2Basic通过Camera2BasicFragment中的状态管理机制,清晰展示了相机从打开到关闭的完整流程:
// 相机初始化关键代码片段
private fun openCamera(width: Int, height: Int) {
val cameraManager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
try {
// 检查相机权限
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestCameraPermission()
return
}
// 打开相机设备
cameraManager.openCamera(mCameraId, mStateCallback, mBackgroundHandler)
} catch (e: CameraAccessException) {
// 异常处理
Log.e(TAG, "openCamera: ${e.message}")
}
}
小贴士:相机操作必须在后台线程执行,避免阻塞UI线程。项目通过
HandlerThread创建了专门的相机操作线程,确保UI流畅性。
2.2 自适应预览实现机制
AutoFitTextureView是项目的一大亮点,它解决了不同屏幕尺寸和比例下相机预览的适配问题。其核心原理是通过重写onMeasure方法,动态调整视图尺寸以匹配相机预览的宽高比。
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val width = MeasureSpec.getSize(widthMeasureSpec)
val height = MeasureSpec.getSize(heightMeasureSpec)
if (0 == mRatioWidth || 0 == mRatioHeight) {
setMeasuredDimension(width, height)
} else {
// 根据预设比例调整视图尺寸
if (width < height * mRatioWidth / mRatioHeight) {
setMeasuredDimension(width, width * mRatioHeight / mRatioWidth)
} else {
setMeasuredDimension(height * mRatioWidth / mRatioHeight, height)
}
}
}
2.3 高效的图像处理与保存策略
ImageSaver类展示了如何在后台线程中处理和保存图像数据,避免阻塞相机捕获流程。它使用Image.Plane获取原始图像数据,并通过BitmapFactory和FileOutputStream将图像保存为JPEG格式。
这种异步处理方式确保了拍照操作不会卡顿,同时通过MediaScannerConnection通知系统媒体库更新,使拍摄的照片能够立即在相册中显示。
三、实践指南:从零开始构建相机应用
3.1 开发环境配置
要开始使用Camera2Basic项目,需要满足以下环境要求:
- Android Studio Arctic Fox或更高版本
- Android SDK API 21及以上
- Kotlin 1.5.0或更高版本
项目获取与构建步骤:
-
克隆项目代码库:
git clone https://gitcode.com/gh_mirrors/an/android-Camera2Basic -
使用Android Studio打开项目,等待Gradle同步完成
-
配置应用模块:确保
kotlinApp/Application/build.gradle中的依赖项正确配置 -
连接Android设备或启动模拟器,点击"Run"按钮构建并安装应用
3.2 核心功能实现详解
3.2.1 相机权限申请
在AndroidManifest.xml中声明必要的权限:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera2.full" />
动态权限请求实现:
private fun requestCameraPermission() {
ActivityCompat.requestPermissions(
activity,
arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE),
REQUEST_CAMERA_PERMISSION
)
}
3.2.2 相机预览实现
-
在布局文件中添加AutoFitTextureView:
<com.example.android.camera2basic.AutoFitTextureView android:id="@+id/texture" android:layout_width="match_parent" android:layout_height="match_parent" /> -
创建相机预览会话:
private fun createCameraPreviewSession() { try { val surfaceTexture = mTextureView.surfaceTexture surfaceTexture.setDefaultBufferSize(mPreviewSize.width, mPreviewSize.height) val surface = Surface(surfaceTexture) // 创建预览请求 mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) mPreviewRequestBuilder.addTarget(surface) // 创建捕获会话 mCameraDevice.createCaptureSession( listOf(surface, mImageReader.surface), object : CameraCaptureSession.StateCallback() { override fun onConfigured(session: CameraCaptureSession) { mCaptureSession = session updatePreview() } override fun onConfigureFailed(session: CameraCaptureSession) { showToast("配置相机失败") } }, null ) } catch (e: CameraAccessException) { e.printStackTrace() } }
3.2.3 拍照功能实现
拍照按钮点击事件处理:
mPhotoButton.setOnClickListener {
takePicture()
}
private fun takePicture() {
if (null == mCameraDevice) {
return
}
val cameraManager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
try {
val characteristics = cameraManager.getCameraCharacteristics(mCameraId)
var jpegSizes: Array<Size>? = null
val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
if (map != null) {
jpegSizes = map.getOutputSizes(ImageFormat.JPEG)
}
// 配置图像尺寸
val width = 640
val height = 480
if (jpegSizes != null && jpegSizes.isNotEmpty()) {
width = jpegSizes[0].width
height = jpegSizes[0].height
}
val reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 2)
val outputSurfaces = mutableListOf<Surface>()
outputSurfaces.add(reader.surface)
outputSurfaces.add(Surface(mTextureView.surfaceTexture))
// 创建拍照请求
val captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
captureBuilder.addTarget(reader.surface)
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
// 设置拍照方向
val rotation = activity.windowManager.defaultDisplay.rotation
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation))
// 拍照回调处理
val readerListener = object : ImageReader.OnImageAvailableListener {
override fun onImageAvailable(reader: ImageReader, image: Image) {
val buffer = image.planes[0].buffer
val bytes = ByteArray(buffer.remaining())
buffer.get(bytes)
// 保存图片
ImageSaver(bytes, File(getOutputDirectory(), "${System.currentTimeMillis()}.jpg")).run()
}
}
reader.setOnImageAvailableListener(readerListener, mBackgroundHandler)
val captureSession = mCaptureSession
captureSession.stopRepeating()
captureSession.abortCaptures()
captureSession.capture(captureBuilder.build(), null, mBackgroundHandler)
} catch (e: CameraAccessException) {
e.printStackTrace()
}
}
3.3 常见问题解决方案
3.3.1 相机预览拉伸变形问题
问题:在不同屏幕尺寸的设备上,相机预览画面出现拉伸或压缩。
解决方案:使用AutoFitTextureView组件,根据相机预览尺寸和屏幕尺寸动态调整视图比例:
// 设置预览尺寸
fun setAspectRatio(width: Int, height: Int) {
if (width < 0 || height < 0) {
throw IllegalArgumentException("尺寸不能为负数")
}
mRatioWidth = width
mRatioHeight = height
requestLayout()
}
在相机初始化时调用:
mTextureView.setAspectRatio(mPreviewSize.width, mPreviewSize.height)
3.3.2 相机资源释放不完整问题
问题:应用退到后台或关闭时,相机资源未正确释放,导致其他应用无法使用相机。
解决方案:在Fragment的生命周期方法中妥善管理相机资源:
override fun onPause() {
super.onPause()
closeCamera()
}
private fun closeCamera() {
mCaptureSession?.stopRepeating()
mCaptureSession?.abortCaptures()
mCaptureSession = null
mCameraDevice?.close()
mCameraDevice = null
}
四、拓展方向:基于Camera2 API的功能升级
4.1 实现手动相机控制
Camera2 API支持丰富的手动控制功能,可以通过以下方式扩展项目:
- 手动对焦:通过
CaptureRequest.CONTROL_AF_MODE设置为CONTROL_AF_MODE_OFF,然后使用LENS_FOCUS_DISTANCE参数控制对焦距离 - 手动曝光:设置
CONTROL_AE_MODE为CONTROL_AE_MODE_OFF,然后通过SENSOR_EXPOSURE_TIME和SENSOR_SENSITIVITY控制曝光 - 手动白平衡:设置
CONTROL_AWB_MODE为CONTROL_AWB_MODE_OFF,然后通过COLOR_CORRECTION_GAINS调整白平衡
4.2 添加视频录制功能
在现有拍照功能基础上,可以通过以下步骤添加视频录制功能:
- 使用
MediaRecorder类处理视频编码 - 创建支持视频录制的
CaptureRequest - 实现录制状态管理和UI控制
- 处理视频文件的保存和预览
4.3 实现实时滤镜效果
要为相机添加实时滤镜,可以使用以下技术方案:
- 使用OpenGL ES创建自定义纹理渲染器
- 将相机预览数据渲染到OpenGL纹理
- 应用GLSL着色器实现滤镜效果
- 将处理后的纹理显示到屏幕
4.4 集成图像分析功能
结合Google的ML Kit,可以为相机应用添加智能分析功能:
- 创建
ImageAnalysis用例处理相机帧数据 - 配置分析器检测人脸、条形码或文本
- 将分析结果实时显示在预览界面上
- 根据分析结果触发相应的应用逻辑
图:Android Camera2Basic应用主界面,显示相机预览区域和底部拍照控制栏,中央的"PICTURE"按钮用于触发拍照操作。
通过android-Camera2Basic项目的学习,开发者不仅能够掌握Camera2 API的基础应用,还能理解Android相机开发的最佳实践。无论是构建简单的拍照应用,还是开发专业的摄影工具,Camera2 API都提供了强大而灵活的基础。随着移动视觉技术的不断发展,掌握相机开发技能将为开发者打开更多创新可能。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedJavaScript095- DDeepSeek-V4-ProDeepSeek-V4-Pro(总参数 1.6 万亿,激活 49B)面向复杂推理和高级编程任务,在代码竞赛、数学推理、Agent 工作流等场景表现优异,性能接近国际前沿闭源模型。Python00
MiMo-V2.5-ProMiMo-V2.5-Pro作为旗舰模型,擅⻓处理复杂Agent任务,单次任务可完成近千次⼯具调⽤与⼗余轮上 下⽂压缩。Python00
GLM-5.1GLM-5.1是智谱迄今最智能的旗舰模型,也是目前全球最强的开源模型。GLM-5.1大大提高了代码能力,在完成长程任务方面提升尤为显著。和此前分钟级交互的模型不同,它能够在一次任务中独立、持续工作超过8小时,期间自主规划、执行、自我进化,最终交付完整的工程级成果。Jinja00
Kimi-K2.6Kimi K2.6 是一款开源的原生多模态智能体模型,在长程编码、编码驱动设计、主动自主执行以及群体任务编排等实用能力方面实现了显著提升。Python00
MiniMax-M2.7MiniMax-M2.7 是我们首个深度参与自身进化过程的模型。M2.7 具备构建复杂智能体应用框架的能力,能够借助智能体团队、复杂技能以及动态工具搜索,完成高度精细的生产力任务。Python00
