Android Camera2Demo深度拆解:从Camera2架构到实战避坑指南
简介Camera2Demo是一份面向Android开发者的Camera2相机封装示例工程聚焦于解决Camera2 API初始化流程复杂、耦合度高的问题。项目将相机配置、会话创建与预览拍照逻辑从Activity中剥离开发者可借助简洁接口快速实现预览、拍照、保存图片等常见操作尤其适合初步接触Camera2或希望快速集成拍照功能的中级开发者参考。压缩包共1087个文件以xml界面与配置、png资源图片、class编译产物和java源码为主同时包含json、dex、apk及gradle构建文件等整体大小约20.81MB。目前已有1291人浏览学习说明其在相机开发场景中具备一定的参考价值。内容上除了完整的Android Studio工程结构还包含可直接运行的测试包与构建脚本便于对照源码理解Camera2的封装思路并在真机上验证拍摄效果。 干过Android相机开发的应该都懂Camera2这套API刚上手的时候有多劝退。名字叫Demo但一眼扫过去回调套回调、状态机套状态机CameraSession还没搞明白CaptureRequest又在那边等着了。我做过的几个项目里凡是能把Camera2Demo真正吃透的人后面接厂商定制相机、自定义拍照流程、甚至做视觉检测输入源基本都不会太吃力。这篇就借Camera2Demo这个项目把Camera2整套运作机制拆开揉碎讲清楚从设计思路到实操细节再到我踩过的那些坑一次性聊透。1. Camera2Demo整体设计与思路拆解1.1 为什么会有Camera2它解决了什么问题先回到Camera1时代。那时候打开相机就三步Camera.open()、setPreviewDisplay()、startPreview()看着简单实际上是个巨大的黑盒。你对焦、曝光、快门速度几乎没有任何手动干预空间想要做点稍微专业的成像功能只能依赖厂商在系统层给你留的那点扩展接口碎片化严重得离谱。Camera2在Android 5.0引入本质是一次彻底的重构不再是一个“开摄像头”的简单入口而是一条从图像传感器到屏幕/文件的管道式架构。这套架构最核心的设计哲学是把控制权还给开发者。传感器、镜头、对焦马达、闪光灯、图像处理器每个物理单元都被抽象成了可配置的组件你可以精确控制曝光时间、ISO、对焦距离、白平衡甚至逐帧调整参数。Camera2Demo作为官方示例的轻量封装恰好把这些能力的入口都暴露了出来所以它表面是个Demo实际是一部“相机控制协议说明书”。1.2 Camera2Demo的核心模块与工作流拿下一个Camera2Demo项目你会看到几个重要的类CameraManager、CameraDevice、CameraCaptureSession、CaptureRequest、ImageReader。它们之间的关系我习惯用“食堂打饭”来类比CameraManager是食堂窗口的“菜单牌”告诉你有哪些窗口摄像头、每个窗口支持什么菜输出格式与尺寸CameraDevice是你选中的那个窗口代表真实打开的一个摄像头CameraCaptureSession是打好菜之后的“取餐流水线”它管理着一组请求的执行顺序CaptureRequest是“点菜单”告诉相机这一帧要怎么拍曝光多少、对焦在哪、存成什么格式ImageReader是“餐盘出口”负责把拍到的图像数据从底层取出来交给上层处理。整个Demo的工作流并不复杂先用CameraManager拿到摄像头列表选择后打开CameraDevice建立CameraCaptureSession然后持续往Session里提交CaptureRequest同时通过ImageReader或SurfaceTexture把图像帧取出来显示或保存。搞清楚这条链路再看代码就不会迷路了。2. 跑通Camera2Demo之前的准备工作2.1 权限与相机特性声明很多人一上来急着写代码结果跑起来直接黑屏或者闪退多半是权限和特性声明没做对。第一件事是在AndroidManifest.xml里加上相机权限uses-permission android:nameandroid.permission.CAMERA / uses-feature android:nameandroid.hardware.camera android:requiredtrue /uses-feature常常被忽略但它其实承担着一个关键作用在Google Play上架时如果你的App核心功能就是拍照却忘了声明这个feature部分没有摄像头的设备也能搜到你等到运行时一调用就崩。另外Android 6.0以上相机权限属于危险权限运行时必须动态申请不能只在Manifest里声明就算完。还有个隐蔽的坑是Android 10开始的分区存储。如果Demo里要把拍照结果保存到MediaStore老代码里那张File路径保存方案在很多机型上会失效。这不是Camera2的问题但确实会让“拍照→保存→显示”这条链路断掉建议一开始就按分区存储的规范来写保存逻辑。2.2 选对输出格式别看了头晕CameraDevice可以同时向多个Surface输出数据每个Surface都有自己期望的格式。Camera2Demo里最常见的做法是一个SurfaceTexture用来预览一个ImageReader用来拍照。有些刚开始接触的人想把拍照和预览都塞到一个Surface里这在Camera2里其实是行不通的系统会因为“输出配置冲突”直接报错。预览输出推荐用SurfaceTexture它本质上是一个生产图像的OpenGL纹理显示效率最高。拍照输出则用ImageReader需求是JPEG就把它配置成ImageFormat.JPEG需求是YUV原始帧就配置成ImageFormat.YUV_420_888想做RAW就配置成RAW_SENSOR。选型原则很简单预览追求低延迟拍照追求高画质两者各走各的通道。ImageReader还有两个参数容易被忽略maxImages和OnImageAvailableListener里的Handler。maxImages控制底层最多缓存几帧图像太小了容易丢帧太大会占用大量内存一般设2到3。Handler则建议用独立的子线程不要用主线程否则图像数据从底层回调上来时如果解码或保存逻辑较重会直接把UI卡死。3. 核心环节会话建立与请求配置3.1 先查能力再动手配置Camera2Demo里最容易诱发崩溃的一个点就是拿到了一个设备根本不支持的尺寸或格式。Camera2的哲学是“你可以控制一切但前提是你得先问系统它给不给”。查询入口是CameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)拿到StreamConfigurationMap之后用getOutputSizes()去拿某个格式支持的尺寸列表。我见过不少Demo代码里直接写死1920x1080在绝大多数手机上没问题但在个别刘海屏或特殊比例的机型上预览画面会被裁剪甚至直接回调失败。稳妥的做法是写一个尺寸选择函数遍历所有支持的尺寸先剔除掉小于屏幕分辨率或比例差距过大的再挑一个面积最大的。这样虽然代码多几行但兼容性会好很多。3.2 CaptureRequest的三种模板选对场景CameraDevice.createCaptureRequest()需要传入一个templateType参数模板类型直接决定相机的底层行为。我常用的有三种TEMPLATE_PREVIEW适合持续预览自动对焦和自动曝光默认开启帧率优先TEMPLATE_STILL_CAPTURE适合单张拍照会针对最高画质做优化但帧率通常不高TEMPLATE_RECORD适合视频录制兼顾帧率与画质。很多人不理解为什么不能一直用STILL_CAPTURE来预览我实测过预览用STILL_CAPTURE模板画质确实好一点但帧率会掉到十几帧滑动画面明显卡顿。原因很简单为了让单张照片达到最佳成像效果底层会拉长曝光时间或做多帧合成这跟“持续流畅显示”的目标天然冲突。模板只是起点你还得往CaptureRequest里添加目标Surface并设置具体参数。比如预览请求要addTarget(previewSurface)拍照请求要addTarget(imageReader.surface)然后通过set()方法设置对焦模式、AE模式等。参数设置的顺序也有讲究CONTROL_AF_MODE这类模式字段要放在CONTROL_AF_TRIGGER之前顺序反了有时候不会报错但实际生效行为会和你预期不一致。3.3 对焦与曝光的手动控制细节Camera2Demo里默认用的对焦模式是CONTROL_AF_MODE_CONTINUOUS_PICTURE这个模式适合大多数场景——半按或点击时自动对焦日常拍照基本不用管。但如果做的项目需要手动对焦比如扫码或微距场景就得切换到CONTROL_AF_MODE_AUTO然后设置CONTROL_AF_REGIONS再触发CONTROL_AF_TRIGGER_START。这里有个非常容易踩的坑触发AF之后必须再发一个不带触发的预览请求否则对焦永远不会生效。原因是Camera2的状态机里AF Trigger是一次性事件发完就等着结果回调但你得让后续请求进入“持续扫描”状态才能保持对焦。我见过很多新手在这个地方卡了一整晚现象就是点击屏幕对焦没反应日志里又死活不报错。曝光控制也一样想手动控制的话先关闭自动曝光设置SENSOR_EXPOSURE_TIME和SENSOR_SENSITIVITY。曝光时间的单位是纳秒1/60秒就是16666666纳秒ISO一般是100到3200之间。这两个参数不是随便设的不同机型有不同上限同样要从CameraCharacteristics里查询硬设一个超范围的值底层会自动截断但成像效果就不可控了。4. 实际调试中常见的问题与排查思路4.1 黑屏和闪退先按这三步排查Camera2Demo跑起来黑屏或闪退80%是下面三个原因第一预览尺寸与展示控件尺寸不匹配。只设置SurfaceTexture的默认缓冲区尺寸忘了在纹理变换矩阵里做适配画面就会变形或黑屏。解决方法是根据预览尺寸和屏幕尺寸的比例关系动态计算矩阵或者选用合适的TextureView/SurfaceView缩放策略。第二会话状态回调没检查。CameraCaptureSession.StateCallback不是摆设Open失败、ConfigureFailed、Active这几个状态都要处理。尤其是onConfigureFailed一旦回调这个方法说明Session的Surface配置有冲突最常见的就是同时提交了两个相同格式但不同尺寸的Surface。别只盯着onOpened其他回调的失败信息往往是关键线索。第三摄像头被占用。前置和后置同时开、其他App还在用相机没有释放都会导致打开摄像头失败。CameraAccessException.CAMERA_IN_USE这个异常字面已经写得很清楚了但日志里很容易忽略。4.2 方向错乱与画面拉伸这个问题在平板和全面屏手机上尤其常见。传感器的物理方向和屏幕方向不一致时不做处理画面就是歪的。正确的做法是通过CameraCharacteristics.SENSOR_ORIENTATION拿到传感器方向再配合当前设备旋转角度计算补偿矩阵而不是简单地在布局里旋转一下TextureView。方向补偿涉及四象限判断每次设备旋转到onConfigurationChanged时都要重新计算。画面拉伸则完全是显示端的问题。Sensor输出的原始比例和屏幕比例不一致直接拉伸填满必然变形。我的处理方法是预览区域内按比例裁剪显示不追求全屏铺满多余部分留黑边或做模糊背景填充。虽然有些设计同学不喜欢黑边但比起人脸被拉宽黑边的可接受度高多了。4.3 对焦闪烁与曝光跳变很多人跑通Demo之后会遇到画面一亮一暗、对焦来回抽动的问题。这多半是因为你在onCaptureCompleted回调里频繁修改了AE/AF相关参数。Camera2的参数设置了不是立刻生效的它在后续请求中异步应用你每一帧都改系统永远在往新状态收敛画面就会“抽搐”。正确的做法是等状态稳定后再触发下一次修改。比如自动对焦完成后CaptureResult里CONTROL_AF_STATE会变成FOCUSED_LOCKED或PASSIVE_SCAN你在这个时机去改变参数或执行拍照逻辑而不是在预览回调里一把梭地改。曝光同理AE的状态变成CONVERGED之后再做下一帧的操作。另外手动曝光时注意先将CONTROL_AE_MODE设为OFF否则你手动设置的曝光参数会被自动曝光覆盖哪怕你明明设了SENSOR_EXPOSURE_TIME最后成像也完全不按你的来。4.4 常见问题速查表现象可能原因排查方式黑屏Surface配置错误或Session配置失败检查onConfigureFailed回调、确认预览Surface已正确addTarget画面拉伸预览尺寸比例与显示区域不一致按比例裁剪显示或动态计算纹理矩阵画面旋转未做传感器方向补偿用SENSOR_ORIENTATION计算补偿矩阵拍照黑图ImageReader的Surface未加入拍照请求确认STILL_CAPTURE请求addTarget了ImageReader.getSurface()崩溃尺寸或格式不支持用StreamConfigurationMap查询后再写入参数对焦无响应触发AF后没有继续提交预览请求设置AF_TRIGGER_START后再提交一个不带触发的请求5. 给Camera2Demo加点实用扩展让它变成真工具5.1 连续拍照加自动保存Camera2Demo默认是一次性拍照但实际项目里产品常常突然给你提“连拍模式”“定时拍”“人脸检测后自动拍”这类需求。实现逻辑并不难核心是循环提交拍照请求在onImageAvailable回调里取走数据之后立刻重新提交一个STILL_CAPTURE请求。需要注意ImageReader的maxImages一定要大于1否则连拍模式下系统会因为缓冲队列满了而不回调新帧。保存JPEG时很多人踩过一个坑从Image.getPlanes()获取的ByteBuffer里每一行的数据可能不是紧密排列的存在rowStride对齐问题。如果你直接按字节顺序读白图、花屏、尺寸错乱都有可能。正确做法是逐行拷贝按rowStride跳过每行末尾的空洞数据只取有效宽度。这个细节在所有调用Camera2直接处理帧数据的场景里都通用。5.2 做一个简单的点击对焦框我做的项目里几乎都要加这个功能体验提升非常明显。实现思路也不复杂用户点击屏幕后把屏幕坐标换算成传感器坐标系下的百分比例再用MeteringRectangle设置CONTROL_AF_REGIONS和CONTROL_AE_REGIONS然后触发一次AF_TRIGGER_START。有个细节是大部分机型的对焦区域并不是越大越好。区域太大相机无法精准聚焦到目标点太小对焦容易失败。我一般取整块传感器区域的1/10到1/5作为对焦框大小兼容度和精准度比较平衡。另外点击对焦触发了AF之后最好把预览请求重新发一遍否则对焦状态不会刷新到新区域上。5.3 拍照静音与连拍节流的处理很多厂商定制项目里“拍照声能不能去掉”是个高频需求。但这里要非常注意在销售区域的法律里多数地区不允许完全静音拍照尤其是涉及隐私的场景。所以我的建议是不要在公共应用里硬做静音功能如果确实需要也要在特定合规前提下由系统权限控制。技术上可以通过设置AudioManager.STREAM_SYSTEM为静音来实现但这是系统层面的事不是Camera2的范畴。连拍节流则是一个很容易被忽略的点。很多人以为把拍照请求连续发出去就行结果发现前几张正常后面越拍越慢甚至卡死。原因是相机的3A状态自动对焦/曝光/白平衡还停留在上一张的收敛状态没有重新触发。我一般在连拍循环里每拍一张就检测一下CONTROL_AF_STATE确认对焦恢复后再发下一张效率虽然低一点但稳定性高很多。最后分享一点我的体会Camera2Demo这套代码说难不难说简单也绝不简单。我见过太多人跑通Demo之后觉得“哦Camera2我懂了”结果一换机型就各种状况有的自动对焦对不上有的预览翻转有的拍出来的照片偏色偏得离谱。真相是Camera2的抽象层级比Camera1厚得多真正的核心不在那几个API调用上而在于理解请求、会话状态、错误回调这三者的循环往复。错误码是排查问题的第一入口onCaptureFailed、onError这些回调里的错误码每个都对应着一类明确的底层异常拿到之后去CameraDevice.StateCallback和CaptureCallback的文档里查基本都能对症下药。所以我的建议永远是先老老实实把官方Demo跑通把每个回调打好日志再考虑封装自己的相机库。这套功夫花得值后面接任何视觉需求都能少踩一半坑。本文还有配套的精品资源点击获取

相关新闻

因数求和算法:从暴力枚举到数论优化的完整指南

因数求和算法:从暴力枚举到数论优化的完整指南

在日常编程练习和算法竞赛中,我们经常会遇到与因数相关的问题。比如,给定一个正整数,如何高效地求出它的所有因数之和?这个问题看似简单,但背后却蕴含着数论的基础原理和巧妙的算法优化技巧。无论是准备面试笔试&#…

2026/9/3 4:20:51 阅读更多 →
Pygame实战:开发一个皮卡丘主题桌面小游戏(附完整代码)

Pygame实战:开发一个皮卡丘主题桌面小游戏(附完整代码)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/3 4:20:51 阅读更多 →
Elasticsearch中文搜索绕不开的IK分词器:安装配置与调优实践

Elasticsearch中文搜索绕不开的IK分词器:安装配置与调优实践

简介:一套适配 Elasticsearch 7.12.1 的中文分词插件 elasticsearch-analysis-ik,面向需要构建中文全文检索能力的搜索开发与运维人员,适用于新闻、电商、社区等大量中文内容处理场景。插件基于 IK Analyzer 提供智能切词与最细粒度两种分词模…

2026/9/3 4:20:51 阅读更多 →

最新新闻

Vue 3组件库打包体积优化:Tree-shaking失效与按需引入实战

Vue 3组件库打包体积优化:Tree-shaking失效与按需引入实战

这次我们来看一个很典型的 Vue 3 打包体积问题:业务项目里只用了组件库 3 个组件,结果打包产物里多出 1.2MB 死代码。这个现象不是组件库本身有问题,而是引用方式、构建配置和 Tree-shaking 是否真正生效共同决定的。很多团队从 Vue 2 Webpa…

2026/9/3 9:06:31 阅读更多 →
Unity与Godot UI架构对比:容器布局、主题资源与工程实践

Unity与Godot UI架构对比:容器布局、主题资源与工程实践

如果你用 Unity 做过几年 UI,心里应该攒了不少“别扭感”。比如界面层级一多,Anchor、Offset、Scale 就开始互相打架;比如一套皮肤想全局换色,要么写一大堆静态配置,要么在预制体里翻半天颜色;比如程序想动…

2026/9/3 9:06:31 阅读更多 →
Python+PyQt5调用百度植物识别API的毕业设计工程实践

Python+PyQt5调用百度植物识别API的毕业设计工程实践

简介:本资源是一套完整的基于Python的植物识别桌面应用毕业设计项目,面向计算机或人工智能方向本科生,解决图像识别类课程设计与毕设中GUI开发、API集成与AI服务调用的实际需求。压缩包共16个文件,含2个核心Python源码&#xff08…

2026/9/3 9:06:31 阅读更多 →
MATLAB实现相移法偏移估计:从原理到代码实战

MATLAB实现相移法偏移估计:从原理到代码实战

简介:本资源是一套面向地球物理勘探专业师生及科研人员的相移法偏移MATLAB实现代码,聚焦地震数据处理中的倾斜界面校正问题,适用于地震成像、速度建模与偏移算法教学实践。压缩包共5个文件,含3个核心.m函数(实现预处理…

2026/9/3 9:06:31 阅读更多 →
ROS2多线激光雷达仿真骨架:Ubuntu22.04+Humble+Fortress全栈验证

ROS2多线激光雷达仿真骨架:Ubuntu22.04+Humble+Fortress全栈验证

简介:本资源是一个面向ROS2初学者与机器人仿真开发者的Gazebo多线激光雷达仿真工程,基于ROS 2 Humble和Ubuntu 22.04平台构建,聚焦于机器人感知模块的快速验证与算法调试,特别适用于SLAM建图、定位导航及传感器融合等场景。压缩包…

2026/9/3 9:06:31 阅读更多 →
Java枚举实战:从字符串常量到业务枚举的完整演进指南

Java枚举实战:从字符串常量到业务枚举的完整演进指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/3 9:05:30 阅读更多 →

日新闻

AI智能体辅助JS逆向:从V8环境搭建到补环境实战

AI智能体辅助JS逆向:从V8环境搭建到补环境实战

先别急着点开,这不是劝退文,而是想讲清楚一件事:用 AI 做逆向值不值得学?如果要用,怎么搭一套“V8 环境 AI 智能体”来提升效率。最近逆向圈、爬虫圈都在聊 AI Agent、AST 工程逆向、JS 逆向这些词,很多新手…

2026/9/3 0:00:29 阅读更多 →
安卓设备通过修改机型信息解锁游戏高帧率:原理、操作与风险指南

安卓设备通过修改机型信息解锁游戏高帧率:原理、操作与风险指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/3 0:00:29 阅读更多 →
ARM版OpenJDK 11安装部署全攻略:下载、配置与避坑指南

ARM版OpenJDK 11安装部署全攻略:下载、配置与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/3 0:00:29 阅读更多 →

周新闻

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析

每年校招季我都会接触不少准备数据库方向笔试的同学,看到最多的状态就是:简历上写着“熟悉 MySQL”“了解索引优化”,一碰到数据库管理工程师的笔试卷,却在索引、事务、锁、备份恢复这些题目上翻车。网易这套 2018 校园招聘数据库…

2026/9/3 4:22:22 阅读更多 →
数字电路时序基石:深入理解建立时间与保持时间

数字电路时序基石:深入理解建立时间与保持时间

1. 这不是“背公式”的事:时间参数到底在约束什么你翻过数字电路教材,一定见过这两个词:建立时间(Setup Time)和保持时间(Hold Time)。它们常被并列写在触发器(Flip-Flop&#xff09…

2026/9/3 4:22:01 阅读更多 →
蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

1. 项目缘起:从赛题到超声波测距机的诞生第八届蓝桥杯单片机设计与开发国赛的题目,我至今记忆犹新。它没有直接给出一个花哨的名字,而是用“超声波测距机”这个朴实无华的功能描述,精准地勾勒出了考核的核心。对于当时备赛的我而言…

2026/9/3 4:22:59 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/3 4:17:49 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/3 4:18:56 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/3 4:21:44 阅读更多 →