Camera权限与配置:AndroidManifest权限声明、动态权限申请、摄像头特性检测、Camera配置参数详解
好,我们正式开始聊摄像头开发的第一道坎——权限与配置。
说实话,很多新手一上来就急着写拍照逻辑,结果App一运行就闪退。为什么?权限没处理好。我当年也犯过这个错,在模拟器上跑得好好的,换到真机上直接崩溃。后来才发现,Android 6.0以后权限模型变了,光在Manifest里声明是不够的。
AndroidManifest权限声明
先说说最基础的。你要用摄像头,得先在AndroidManifest.xml里告诉系统:我要用这个硬件。
<uses-permission android:name="android.permission.CAMERA" />
就这一行?嗯,基本是这样。但有个细节很多人忽略——如果你只是想让有摄像头的设备才能安装你的App,可以加个uses-feature标签:
<uses-feature android:name="android.hardware.camera" android:required="true" />
设为true的话,没有摄像头的设备在Google Play上根本搜不到你的App。我个人习惯是设为false,然后在代码里动态检测。这样平板用户也能装,只是拍照功能不可用而已。
重要提醒:从Android 10(API 29)开始,如果你用Camera2 API,还需要声明android.permission.CAMERA。但如果你只是用系统相机Intent拍照,不需要这个权限——系统会帮你处理。
动态权限申请
好,Manifest写完了,你以为就完了?不,这只是第一步。
Android 6.0(API 23)引入了运行时权限模型。说白了,敏感权限不能只在安装时问一次,必须在运行时弹窗让用户确认。摄像头权限就是典型的敏感权限。
我见过太多人把权限申请写得特别复杂。其实核心就三步:
- 检查权限是否已授予——用
ContextCompat.checkSelfPermission() - 如果没有,发起请求——用
ActivityCompat.requestPermissions() - 处理回调结果——重写
onRequestPermissionsResult()
看代码:
// 检查权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
// 权限未授予,发起请求
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
CAMERA_PERMISSION_CODE);
} else {
// 权限已授予,直接打开摄像头
openCamera();
}
// 处理回调
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == CAMERA_PERMISSION_CODE) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openCamera();
} else {
// 用户拒绝了权限
Toast.makeText(this, "需要摄像头权限才能拍照", Toast.LENGTH_SHORT).show();
}
}
}
小技巧:如果用户第一次拒绝,第二次再弹窗时可以加个解释。用shouldShowRequestPermissionRationale()判断是否需要向用户说明为什么需要这个权限。我曾经在一个项目中没加这个判断,用户连续拒绝两次后直接勾选了"不再询问",后面就再也弹不出权限申请了——只能让用户去设置里手动开启。
摄像头特性检测
你想想看,如果设备根本没有摄像头,你申请权限有什么用?所以,在申请权限之前,最好先检测一下设备是否真的有摄像头硬件。
为什么会有设备没有摄像头?嗯,Android设备种类太多了。有些平板、电视盒子、车载设备真的没有摄像头。我就在一个智能电视项目上踩过这个坑——代码里直接调了摄像头,结果在电视上崩溃了。
检测方法很简单:
PackageManager pm = getPackageManager();
if (pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
// 有摄像头
} else {
// 没有摄像头,隐藏拍照按钮
}
如果你想检测是否有前置摄像头:
if (pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
// 有前置摄像头
}
还可以检测闪光灯:
if (pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {
// 有闪光灯
}
注意:hasSystemFeature()检查的是硬件特性,不是权限。即使你没有申请CAMERA权限,这个方法也能正常调用。所以它适合放在权限申请之前做预检。
Camera配置参数详解
好,权限有了,硬件也确认了,接下来就是配置摄像头了。这里我主要讲Camera1 API的参数配置,因为Camera2的配置更复杂,我们后面会单独讲。
Camera1 API的核心是Camera.Parameters类。拿到这个对象后,你可以做很多事:
Camera camera = Camera.open();
Camera.Parameters params = camera.getParameters();
// 设置预览尺寸
params.setPreviewSize(1280, 720);
// 设置拍照尺寸
params.setPictureSize(1920, 1080);
// 设置对焦模式
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
// 设置白平衡
params.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
// 设置闪光灯模式
params.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
camera.setParameters(params);
这里有几个关键参数,我挑重点说:
| 参数 | 说明 | 常用值 |
|---|---|---|
| 预览尺寸 | 取景器显示的尺寸,不是最终照片尺寸 | 1280x720, 1920x1080 |
| 拍照尺寸 | 最终照片的分辨率 | 1920x1080, 3264x2448 |
| 对焦模式 | 控制摄像头如何对焦 | AUTO, CONTINUOUS_PICTURE, MACRO |
| 白平衡 | 调整色温,让颜色更自然 | AUTO, DAYLIGHT, CLOUDY, FLUORESCENT |
| 闪光灯模式 | 控制闪光灯行为 | AUTO, ON, OFF, TORCH |
避坑指南:设置参数前,一定要先检查设备是否支持该参数。比如不是所有摄像头都支持CONTINUOUS_PICTURE对焦模式。我曾经在低端机上直接设置了这个模式,结果setParameters()抛了个异常。正确的做法是先用getSupportedFocusModes()获取支持的模式列表,再从中选择一个。
List<String> supportedFocusModes = params.getSupportedFocusModes();
if (supportedFocusModes != null
&& supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
} else if (supportedFocusModes != null
&& supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
同样的逻辑也适用于预览尺寸和拍照尺寸。先获取支持列表,再选一个最合适的。我个人习惯是优先选16:9的比例,因为现在大部分屏幕都是16:9的,预览不会变形。
知识体系总览
说了这么多,我把这一章的核心逻辑画成了一张图,方便你理解整个流程:
这张图把整个流程串起来了。你从左上角开始,按箭头方向走,每一步都有对应的代码实现。记住,每一步都可能失败——Manifest没写、硬件不支持、用户拒绝权限、参数不兼容——所以每一步都要做异常处理。
嗯,这一章的内容就这些。权限和配置是摄像头开发的基础,基础打牢了,后面写拍照逻辑才不容易出问题。下一章我们开始写真正的拍照代码。