首页
/ 如何高效掌握Android Camera2 API开发?从基础到实战的进阶指南

如何高效掌握Android Camera2 API开发?从基础到实战的进阶指南

2026-04-21 10:51:06作者:房伟宁

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获取原始图像数据,并通过BitmapFactoryFileOutputStream将图像保存为JPEG格式。

这种异步处理方式确保了拍照操作不会卡顿,同时通过MediaScannerConnection通知系统媒体库更新,使拍摄的照片能够立即在相册中显示。

三、实践指南:从零开始构建相机应用

3.1 开发环境配置

要开始使用Camera2Basic项目,需要满足以下环境要求:

  • Android Studio Arctic Fox或更高版本
  • Android SDK API 21及以上
  • Kotlin 1.5.0或更高版本

项目获取与构建步骤

  1. 克隆项目代码库:

    git clone https://gitcode.com/gh_mirrors/an/android-Camera2Basic
    
  2. 使用Android Studio打开项目,等待Gradle同步完成

  3. 配置应用模块:确保kotlinApp/Application/build.gradle中的依赖项正确配置

  4. 连接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 相机预览实现

  1. 在布局文件中添加AutoFitTextureView:

    <com.example.android.camera2basic.AutoFitTextureView
        android:id="@+id/texture"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
  2. 创建相机预览会话:

    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_MODECONTROL_AE_MODE_OFF,然后通过SENSOR_EXPOSURE_TIMESENSOR_SENSITIVITY控制曝光
  • 手动白平衡:设置CONTROL_AWB_MODECONTROL_AWB_MODE_OFF,然后通过COLOR_CORRECTION_GAINS调整白平衡

4.2 添加视频录制功能

在现有拍照功能基础上,可以通过以下步骤添加视频录制功能:

  1. 使用MediaRecorder类处理视频编码
  2. 创建支持视频录制的CaptureRequest
  3. 实现录制状态管理和UI控制
  4. 处理视频文件的保存和预览

4.3 实现实时滤镜效果

要为相机添加实时滤镜,可以使用以下技术方案:

  1. 使用OpenGL ES创建自定义纹理渲染器
  2. 将相机预览数据渲染到OpenGL纹理
  3. 应用GLSL着色器实现滤镜效果
  4. 将处理后的纹理显示到屏幕

4.4 集成图像分析功能

结合Google的ML Kit,可以为相机应用添加智能分析功能:

  1. 创建ImageAnalysis用例处理相机帧数据
  2. 配置分析器检测人脸、条形码或文本
  3. 将分析结果实时显示在预览界面上
  4. 根据分析结果触发相应的应用逻辑

Android Camera2Basic应用界面

图:Android Camera2Basic应用主界面,显示相机预览区域和底部拍照控制栏,中央的"PICTURE"按钮用于触发拍照操作。

通过android-Camera2Basic项目的学习,开发者不仅能够掌握Camera2 API的基础应用,还能理解Android相机开发的最佳实践。无论是构建简单的拍照应用,还是开发专业的摄影工具,Camera2 API都提供了强大而灵活的基础。随着移动视觉技术的不断发展,掌握相机开发技能将为开发者打开更多创新可能。

登录后查看全文
热门项目推荐
相关项目推荐