Android摄像头开发全攻略:API选型、核心实现与避坑指南
2026/9/9 12:05:00 网站建设 项目流程

简介:面向Android开发者的摄像头功能完整工程资源,覆盖从Camera1到Camera2 API的典型调用方式,适合正在学习Android拍照、视频录制及自定义相机应用的初中级开发者。工程基于实际项目整理,直接解决摄像头权限申请、预览画面旋转、多摄像头切换、闪光灯控制等常见难点。资源包共470个文件,以java源码、xml布局与配置、class编译文件及png资源图片为主,压缩包仅4.69MB,便于快速导入Android Studio学习改造。已有6083人学习使用,内容从基础预览到高级特性(如YUV数据处理、人脸检测)均有示例,可帮助理解Android相机架构并避开常见坑点。无论以官方Camera2Basic入门,还是参考第三方库思路,都能获得可直接运行的参考实现。 做Android开发五年多,摄像头这块的坑也算踩了个遍。手里攒下的代码和经验,光相机回调那一堆状态机就能把人绕晕。今天这篇不是介绍什么黑科技,而是把Android摄像头软件功能这件事完整拆开:从最基础的预览、拍照、录像,到对焦、变焦、闪光灯、人脸检测,再到CameraX和Camera2怎么选、权限和生命周期怎么避坑,以及模拟器虚拟摄像头、智能车摄像头这类特殊场景怎么接入。无论你是刚入门想给App加个扫码功能,还是想折腾自己的智能硬件项目,这篇都能提供一个可以直接落地的思路。

1. Android摄像头功能全景:一个摄像头到底能榨出多少功能?

从一个用户视角看,摄像头功能就是打开相机、拍照、录像。但从开发者角度看,Android摄像头软件功能可以拆成三层:图像采集层、处理层和应用层。图像采集层负责从传感器拿到原始数据,处理层负责格式转换、降噪、人脸框选、HDR合成,应用层则是你看到的预览画面、拍照按钮、滤镜、美颜这些。做全功能,实际上就是把这三层涉及的API都用到。

Android官方对摄像头的支持经历了三代:最早是Camera(也被称为Camera1),用android.hardware.Camera,老项目里还在用;Android 5.0开始引入Camera2,用android.hardware.camera2包,功能强大但API复杂,回调一大堆;再到2019年Google推出Jetpack CameraX,基于Camera2封装,用ViewModel那样的生命周期感知方式简化了开发。我的直观感受是:Camera1就像是手动挡,最早一手握方向盘一手挂挡,能跑但挂错挡就熄火;Camera2是专业单反的手动拨盘,参数全但新手容易蒙;CameraX则像是带辅助驾驶的自动挡,把摄像头的常规能力封装成容易调用的接口,同时保留了对Camera2特性的兼容。

所以现在新项目我基本无脑选CameraX处理常规功能,只有需要手动控制传感器级别参数时才去写Camera2的CameraCaptureSession。遇到特殊硬件,比如热词里提到的树莓派OV5647模块、OV7670车牌识别,这些在Android上往往要么走UVCCamera库,要么走JNI封装,与系统相机框架关系不大,后面我会讲。

1.1 手机摄像头不只是“拍照”

很多人以为摄像头功能就是拍照录像,其实一个完整的多媒体应用涉及的摄像头能力包括:预览、静态拍照、视频录制、对焦(自动/手动)、变焦(光学/数字)、闪光灯控制(开关、自动、手电筒)、曝光补偿、白平衡、ISO、人脸检测、场景识别、RAW输出、HDR、连拍、延时摄影、慢动作。其中预览是所有功能的基础,拍照和录像都是基于预览流做的。对焦、变焦、闪光灯是拍摄体验的关键,如果这几个做不好,App的用户体验会直线下降。

另外,摄像头并不能独立工作,它要配合传感器方向、设备旋转角度、预览尺寸和相册图片的方向。很多新手会碰到拍出来的照片旋转90度,就是没处理SensorOrientation。Android设备的摄像头传感器通常固定为90度或270度,预览和拍照要分别做方向矫正。这个属于摄像头软件功能的隐藏必修课。

我曾在某电商App上处理过商品拍摄模块,看起来只有拍照,但涉及正面/背面摄像头切换、二维码识别、光线太暗自动开闪光灯、低分辨率预览但高分辨率存储等需求,所以哪怕是“拍照”这一个功能,也要考虑场景上下文的适配。

1.2 Camera、Camera2、CameraX:三代API的演进逻辑

Camera1的API简单,open()然后setPreviewDisplay()就能预览,但它是单线程同步模型,很多操作必须按顺序来,稍微复杂点的场景就卡顿。Camera2引入的CameraDevice、CaptureRequest、CaptureSession模型本质上是把一次拍照变成了“提交请求-队列执行-回调完成”的异步管道,支持快速连拍、RAW输出、手动控制曝光和焦点,但调试门槛直线上升。CameraX则是在Camera2之上做了一个官方兼容层,内置生命周期感知,自动处理旋转、缩放和摄像头切换,例如我们想实现一个带拍照的基础相机,用CameraX几十行代码就能跑通,而用Camera2至少要写6个回调。

如果你的项目要长期维护,选CameraX能省掉不少心力。Google也给出了明确的演进态度:Camera1在旧设备还有兼容性问题,但官方已不在新版本更新它;Camera2仍然维护;CameraX是未来方向。所以我的建议是:新项目一律CameraX,老项目维护先用兼容层适配。CameraX还有一个好处是支持SetTargetRotation,让我们不用自己算SensorOrientation,这个坑很浅但很容易踩。

2. 项目实战:怎么选API和配置参数?

2.1 选CameraX还是Camera2?我的决策公式

这句话更像实际项目里的灵魂拷问。我的选择逻辑是:如果业务里需要连续自动对焦、人脸追踪、实时滤镜,而且产品要上线到几十款Android机型,优先CameraX;如果要做专业相机、实验室数据采集、需要控制每帧曝光时间或RAW拍摄,用Camera2裸写。理由是CameraX内部封装了设备不兼容的适配逻辑,比如不同厂商的性能模式、画面旋转、生命周期重启,这些用Camera2自己处理会非常痛苦。我经历过一次录制视频在大法手机上黑屏、在蓝绿厂手机上掉帧的问题,后来迁到CameraX后这些问题基本消失。

具体决策公式化:常规功能 + 快速迭代 = CameraX;深度控制 + 硬件闭环 = Camera2;两者也可以混合,但不要混在一个页面上——比较推荐的方式是整个相机界面用CameraX,需要特殊参数时通过Camera2CameraControl获取底层CameraDevice做定制。

2.2 分辨率、帧率和画质参数怎么定?

很多同学喜欢直接写死1920x1080,结果在低端机上卡成PPT。分辨率并不是越高越好,要结合传感器输出能力、预览Surface类型、内存带宽和编码器。框架上,CameraX的预览分析使用了ImageAnalysis,这让我们能自定义分辨率,但注意预览尺寸和拍照尺寸是两个维度:预览尺寸取决于屏幕比例,一般选和屏幕长宽比一致的;拍照尺寸则选择传感器支持的最高或接近目标像素的尺寸,通常4:3的12MP到48MP之间。

帧率方面,普通预览30fps够了,如果做扫码或消除抖动,可以看Sensor的fps范围。通过Camera2的SENSOR_INFO_ACTIVE_ARRAY_SIZE和SCALER_STREAMCONFIGURATION_MAP可以查支持的分辨率组合。CameraX选分辨率时可以用AspectRatio.RATIO_4_3或RATIO_16_9配合ResolutionSelector。开发时我一般先拿到CameraX内部支持的分辨率列表(通过Camera2CameraInfo里拿StreamConfigurationMap),再过滤出比例接近的实际尺寸。这样可以避免黑边。

2.3 预览尺寸和拍照尺寸的适配

预览Surface是TextureView或PreviewView,它需要和屏幕的像素尺寸以及预览流尺寸对应。PhotoCapabilities里有个方法会返回预览尺寸范围。很多人以为把预览比例设成16:9就没问题,但不同手机屏幕是19.5:9,导致上下有黑边,这是正常的,最优雅的做法是让预览铺满屏幕、允许裁剪边缘,因为用户注意力集中在中心,裁掉一点无关紧要。拍照尺寸则要保持比例一致,否则拍出来的照片旋转或拉伸。在CameraX里,PreviewView会自动处理中心裁剪模式(ScaleType.FILL_CENTER),不需要你额外算啊算。

3. 核心功能实现:预览、拍照、录像、对焦和变焦一次打通

3.1 预览:CameraX的Preview和SurfaceProvider

先做预览初始化。CameraX预览步骤:创建ProcessCameraProvider,绑定生命周期,为Preview设置SurfaceProvider,然后开启。代码大概是这样:

val preview = Preview.Builder() .setTargetRotation(display.rotation) .build() preview.setSurfaceProvider(binding.previewView.surfaceProvider) cameraProvider.bindToLifecycle(this, CameraSelector.DEFAULT_BACK_CAMERA, preview)

这段代码背后做了很多事:打开了相机设备、创建了Session、处理了旋转、还要和生命周期联动。所以如果你用Activity,绑定到lifecycleOwner;如果用Service,就得手动管理。常见坑:忘记bindToLifecycle或者重复bind,会报“Camera is already bound”错误。避免方式是在每个Activity/Fragment的onCreate里只绑定一次,onDestroy由lifecycle自动处理。

3.2 拍照:TakePicture的两种模式

CameraX的ImageCapture有两种模式:最小延迟模式(CAPTURE_MODE_MINIMIZE_LATENCY)和高质量模式(CAPTURE_MODE_MAXIMIZE_QUALITY)。前者适合连拍,后者适合相机App的默认拍照,因为按下快门后回显更快。拍照核心代码是bindToLifecycle时加入ImageCapture,然后调用capture:

val imageCapture = ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY) .build() // 拍照 imageCapture.takePicture(ctx, executor, object : ImageCapture.OnImageSavedCallback { override fun onImageSaved(output: ImageCapture.OutputFileResults) { val uri = output.savedUri // 这里拿到的是JPEG的Uri } override fun onError(exc: ImageCaptureException) { ... } })

注意takePicture有两种重载:一个直接保存到文件,一个返回ImageProxy让我们能做旋转、水印、人脸框等后处理。如果要加水印或滤镜,用ImageAnalyzer配ImageProxy处理,但高频处理也会导致卡顿,建议尺寸降一倍。

3.3 录像:VideoCapture与音视频合成

录像可能是Android摄像里兼容性问题最多的点。CameraX当前的VideoCapture使用MediaRecorder或MediaCodec封装,绑定方式一样:

val videoCapture = VideoCapture.withOutput( VideoCapture.Output.FileOutputOptions.Builder(file) .build() ) val camera = cameraProvider.bindToLifecycle(..., preview, imageCapture, videoCapture) // 开始录制 videoCapture.start(fileOutputOptions, executor, callback) // 停止 videoCapture.stop(callback)

录出来的视频默认是MP4,音频从麦克风采集。需要特别留意的坑:切换摄像头、录制中熄屏、旋转屏幕都会导致录制中断或文件损坏。我自己遇到比较多的现象是三星设备上启动录像黑屏,后来发现是预览尺寸和VideoCapture输出尺寸不匹配,CameraX虽然会自动协调,但还是要确保预览比例与目标视频比例一致。另一个坑是MediaRecorder的AudioSource不能用VOICE_CALL,会报权限错误,普通采音用CAMCORDER。

3.4 对焦和变焦:AF/AE模式的配置和缩放控制

自动对焦是Camera2里的CONTROL_AF_MODE,CameraX里做了封装,默认是Continuous。打开相机后,如果你想实现点屏对焦,可以用CameraControl.startFocusAndMetering:

val focusMeteringAction = FocusMeteringAction.Builder( MeteringPointFactory.fromPoint(previewView, x, y).createPoint(x, y) ).build() cameraControl.startFocusAndMetering(focusMeteringAction)

这个会触发对焦并设置曝光区域。变焦更简单:cameraControl.setLinearZoom(0.5f),0代表最广角,1代表最大变焦。这里要特别注意一点:单独用setZoomRatio可能在某些机型上不稳,推荐用setLinearZoom,它对焦距变化的映射统一了,iOS的AVFoundation也是类似做法。另外闪光灯模式通过cameraControl.enableTorch(true)实现手电筒功能,注意如果摄像头被其他App占用,enableTorch会抛异常。

4. 避坑实录:从权限到生命周期,那些文档里不写的坑

4.1 权限与运行时授权:为什么摄像头初始化总是失败

Android 6.0以上需要动态申请CAMERA权限,如果用户拒绝,摄像头打开就报错。很多同学在onRequestPermissionsResult里忘记处理permanently denied(勾选“不再询问”)的情况。我总结的权限判断套路:先检查ContextCompat.checkSelfPermission;然后请求;拒绝后再弹引导去设置页。另外,如果你要录像并保存音频,还需要RECORD_AUDIO权限,两个权限要一起申请。如果只拍照不录音,不要申请录音权限,审核会卡。还有一个隐蔽坑:同一时刻只能有一个应用绑定摄像头,如果你用模拟器测试,主机打开了摄像头,模拟器里就会显示“正在使用摄像头”,这个不是代码问题。

4.2 生命周期绑定与释放:旋转、后台导致的崩溃

摄像头是独占资源,onPause时如果还持有Session,可能被系统强杀或黑屏。CameraX解决了这个问题,但有个细节:bindToLifecycle传入的LifecycleOwner必须在onDestroy前完成解绑。在Fragment里常用mLifecycleOwner,严禁在onCreateView里用activity作为owner而Fragment被销毁却不解绑,否则会出现“Camera is in use”或内部泄漏。实测中我还遇到过屏幕旋转时PreviewView重建,导致surfaceProvider重新设置,这时CameraX会认为你更新了Surface并自动重新配置,但如果你在onDestroy中还调用cameraProvider.unbindAll(),就会重新触发一次绑定流程,产生闪烁。建议用withStateFlow或把相机页面固定为竖屏,避免旋转折腾。

4.3 Android Studio连接小米手机等真机调试问题

摄像头开发必须上真机,模拟器过于理想化。之前有人问Android Studio怎么连接小米手机,这个流程其实不难:先打开开发者选项,点击“MIUI版本号”7次,进入开发者选项,打开“USB调试”,再打开“USB安装”。然后电脑上装好ADB驱动,手机连接后在Android Studio的设备列表能看到。常见问题:小米手机默认把USB模式设成了“仅充电”,要手动改成“文件传输”或“传送文件”;还有一个是MIUI安全限制,需要在手机端弹窗点允许USB调试。连接后可以用adb devices验证。

真机调试摄像头还有个额外建议,尽量准备2-3台不同系统的机器,因为有些高版本API在不同机型行为差异很大,尤其老的三星、华为以及最新Google Pixel。

4.4 模拟器没有真实摄像头怎么办?虚拟摄像头配置指南

如果只是想快速验证界面逻辑,Android模拟器支持模拟摄像头。配置方法是:在AVD Manager里,点编辑设备的摄像头选项,将“Back”和“Front”改为“VirtualScene”或“Emulated”。用VirtualScene模式时,模拟器会显示一个动态变化的3D场景,适合验证预览和拍照流程。另外也可以用虚拟摄像头插件,在Windows上装了虚拟摄像头驱动后,Android Studio模拟器也能调用它作为输入源。不过虚拟摄像头没法模拟真实的光线、对焦、噪声,所以只能用于UI/流程测试,不能作为图像算法验证环境。

5. 特殊场景扩展:虚拟摄像头、树莓派与智能车

5.1 用虚拟摄像头在模拟器里模拟各种摄像头

在Android模拟器上选择“Emulated”摄像头时,系统会提供一张棋盘格图片作为预览源,适合测试相机调用的报文格式。热词里提到“安卓模拟器使用虚拟摄像头”,其实这个做法在自动化测试时很实用:不用真机也能跑UI测试。配置虚拟摄像头驱动后,你甚至可以在电脑上播放一段MP4,然后模拟器把它当成摄像头画面,这样可以在CI环境里跑拍照用例。但需要注意,虚拟摄像头的输出色彩空间和真实设备有差异,AdobeRGB、YUV格式表现不同,如果你的算法依赖色彩信息,还是要用真实设备。

5.2 树莓派OV5647的Android接入思路

树莓派官方摄像头模块OV5647是CSI接口,通常配合树莓派跑Linux系统,但有人想在Android设备上接类似模块。其实有两种路线:一是用UVC免驱摄像头,通过USB接Android设备,Android 5.0以上支持UVC,应用层可用libyuv或UVCCamera库打开;另一种是OV5647这种ISP数据输出,需要在Android底层写Camera HAL模块,不是普通App开发者能快速搞定的。除非你正在做系统开发,否则建议放弃在Android上直接驱动CSI摄像头,改成树莓派采集画面、通过RTSP推流,Android端用MediaPlayer或者ExoPlayer拉流播放。这个方案我在智能车项目里用过,稳定度远超直接在手机上改驱动。

5.3 智能车摄像头搜线与补线的算法基础

热搜词里还有“智能车摄像头搜线”“十字补线”,这是全国大学生智能汽车竞赛里常见的图像处理任务。摄像头任务本质就是用二值化图像区分赛道边缘和背景,然后通过行扫描找左右边界线的跳变点,再通过最小二乘法拟合中心线。所谓“搜线”一般先从中间行开始向左右搜索,遇到黑白跳变就是边界,找到一组点后对边缘点做线性拟合。如果赛道出现十字,左右边线会出现交叉,这时候要用“补线”策略,一般是根据历史数据预测丢失的线,或者用最近几帧的边缘点做外推。这在Android端实现的话,可以把摄像头图像转成Bitmap,用OpenCV的Canny提取边缘,再用概率霍夫变换找线段。跑在手机上性能会比较紧张,所以通常把分辨率降一半再处理。

最后分享两个我实际操作中的体会。第一,做摄像头功能最重要的不是把API背熟,而是要先想清楚你的目标帧率、分辨率、存储格式和业务场景,比如扫码App就不应该用48M拍照,低分辨率反而提升扫码速度。第二,遇到摄像头黑屏或闪退,优先检查权限和生命周期,别一上来就怀疑算法。把CameraX的示例项目跑通,再去改自己的业务代码,效率会高很多。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询