Camera1 API 核心方法详解:从打开到拍照的完整流程
各位同学,今天我们来啃 Camera1 API 这块硬骨头。说实话,现在新项目基本都用 CameraX 了,但你要是去维护老项目、做 ROM 开发、或者搞一些定制化需求——Camera1 你绕不开。我自己刚入行那会儿,被 Camera1 的 callback 机制坑过好几次,今天就把这些经验全抖出来。
1. Camera.open() —— 一切从这里开始
打开摄像头,就这么简单?嗯,表面上是这样。但背后有不少门道。
Camera camera = Camera.open(); // 打开后置摄像头
Camera camera = Camera.open(0); // 显式指定摄像头 ID
我个人习惯用带参数的版本。为什么?因为有些设备上,Camera.open() 默认打开的是前置摄像头——你想想看,这要是做扫码功能,画面一片漆黑,用户不骂娘才怪。
另外,记得检查摄像头是否存在:
int cameraCount = Camera.getNumberOfCameras();
if (cameraCount == 0) {
// 没有摄像头,别硬来
return;
}
2. setParameters() —— 调参的艺术
打开摄像头之后,第一件事就是配参数。说白了,就是告诉摄像头:「我要多大分辨率、要不要闪光灯、对焦模式怎么搞」。
Camera.Parameters params = camera.getParameters();
params.setPictureSize(1920, 1080);
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
camera.setParameters(params);
这里有个坑——不是所有参数都支持。你设了一个不支持的参数,setParameters() 直接抛异常。我早期写代码时就被这个坑过,后来养成了习惯:
List<Size> supportedSizes = params.getSupportedPictureSizes();
// 遍历找到最合适的尺寸,别硬编码
3. setPreviewDisplay() —— 把画面「投」到屏幕上
摄像头采集到的数据,总得有个地方显示吧?setPreviewDisplay() 就是干这个的。它需要一个 SurfaceHolder,通常来自 SurfaceView。
SurfaceView surfaceView = findViewById(R.id.surface_view);
SurfaceHolder holder = surfaceView.getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
// Surface 准备好了,可以设置预览
try {
camera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
}
// ... 其他回调
});
注意顺序:先等 Surface 创建完成,再 setPreviewDisplay。你要是反过来,摄像头数据来了但没地方画,画面就是黑的。
4. startPreview() —— 让画面动起来
参数配好了,显示目标也设了,该让摄像头干活了。
camera.startPreview();
就一行?对,就一行。但这一行背后,摄像头开始疯狂采集数据、送数据到 Surface、刷新画面。你想想看,每秒 30 帧的画面,背后是硬件和软件的高强度配合。
5. takePicture() 的回调机制 —— 最绕的部分来了
拍照不是「咔嚓」一下就完事的。它有三个回调,按顺序触发:
| 回调 | 触发时机 | 数据内容 |
|---|---|---|
| ShutterCallback | 按下快门瞬间 | 无数据,只做通知(比如播放快门声) |
| PictureCallback (raw) | 原始数据可用时 | 未压缩的 RAW 数据(可能为 null) |
| PictureCallback (jpeg) | JPEG 编码完成 | 最终的 JPEG 字节数组 |
camera.takePicture(shutterCallback, rawCallback, jpegCallback);
// 实际项目中,我通常只关心 JPEG 回调
Camera.PictureCallback jpegCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
// data 就是 JPEG 图片的字节流
// 可以保存到文件、上传服务器、或者显示在 ImageView 上
saveImageToFile(data);
camera.startPreview(); // 别忘了重新开始预览!
}
};
这里有个关键点:拍照后预览会自动停止。你要是不重新调 startPreview(),画面就卡住了。我见过不少新手在这栽跟头——拍了一张照片后,预览画面不动了,以为手机死机了。
知识体系总览
下面这张图,把 Camera1 的核心流程串起来了。你照着这个顺序写代码,基本不会出大问题。
总结一下
Camera1 的这五个方法,说白了就是一条流水线:开设备 → 调参数 → 绑屏幕 → 开预览 → 拍照收数据。每一步都有坑,但只要你按顺序来,注意回调里的线程问题,基本稳了。
我个人建议,初学者先把 takePicture() 的 JPEG 回调玩熟。别一上来就搞 raw 数据——那玩意儿处理起来麻烦,而且很多设备根本不支持。先把照片拍出来、存下来,成就感有了,后面再慢慢深入。
camera.release() 一定要在 finally 块里调,不然下次打开摄像头会报错。我早期写代码时漏过几次,结果测试同事天天找我:「摄像头打不开了!」——尴尬。
公众号:蓝海资料掘金营,微信deep3321