Android 7相机调用崩溃及图片裁剪报错问题求助
我来帮你拆解这两个问题,都是Android版本升级后常见的兼容性坑,咱们一步步来解决:
一、Camera.open()强制关闭的问题
问题原因
Android 6.0(API 23)起引入了动态权限机制,CAMERA属于危险权限,必须在运行时主动申请用户授权;另外高版本系统对相机资源的管控更严格,若相机被其他应用占用、设备无可用相机,直接调用Camera.open()会直接崩溃。而Android 4.4.2不需要动态权限,所以之前能正常运行。
解决办法
先申请相机权限
在调用相机前,先检查是否已获取CAMERA权限,没有的话动态申请:private static final int REQUEST_CAMERA_PERMISSION = 100; // 检查并申请相机权限 private void checkCameraPermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION); } else { // 权限已获取,打开相机 openCameraSafely(); } } // 处理权限申请结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_CAMERA_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { openCameraSafely(); } else { Toast.makeText(this, "需要相机权限才能使用该功能", Toast.LENGTH_SHORT).show(); } } }安全打开相机实例
用try-catch包裹相机打开逻辑,避免因相机被占用等情况崩溃,还可以遍历可用相机ID尝试打开:private void openCameraSafely() { Camera camera = null; try { // 先尝试打开默认相机(ID=0) camera = Camera.open(); // 后续相机操作... } catch (Exception e) { // 尝试打开其他相机ID(比如ID=1,前置相机) try { camera = Camera.open(1); } catch (Exception ex) { Toast.makeText(this, "无法打开相机,请检查相机是否被占用", Toast.LENGTH_SHORT).show(); ex.printStackTrace(); } } }
二、相册选图裁剪时“Editing is not supported”错误
问题原因
Android 7.0(API 24)起,系统禁止应用向其他应用暴露file://格式的Uri,否则会抛出FileUriExposedException;同时部分系统自带的裁剪应用不支持接收file:// Uri,导致弹出“Editing is not supported”错误。而Android 4.4.2没有这个限制,所以之前正常。
解决办法
核心是用FileProvider生成content://格式的Uri来替代file://,具体步骤如下:
配置FileProvider
在AndroidManifest.xml的<application>标签内添加FileProvider配置:<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>(如果用的是support库,把
androidx.core.content.FileProvider换成android.support.v4.content.FileProvider)然后在
res/xml目录下创建file_paths.xml文件,指定允许共享的文件目录:<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="my_images" path="Pictures/" /> <external-cache-path name="cache_images" path="Pictures/" /> <!-- 根据你的需求添加其他目录,比如external-path对应SD卡根目录 --> </paths>修改裁剪Intent的Uri处理
选择图片后,生成裁剪输出文件的Uri时,用FileProvider获取content://格式的Uri,并给裁剪Intent添加读写权限:private static final int REQUEST_CROP = 200; // 假设已从相册获取图片的inputUri private void startCrop(Uri inputUri) { // 创建裁剪输出文件 File outputFile = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "cropped_" + System.currentTimeMillis() + ".jpg"); Uri outputUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", outputFile); Intent cropIntent = new Intent("com.android.camera.action.CROP"); cropIntent.setDataAndType(inputUri, "image/*"); // 添加读写权限 cropIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); cropIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // 设置裁剪参数 cropIntent.putExtra("crop", "true"); cropIntent.putExtra("aspectX", 1); cropIntent.putExtra("aspectY", 1); cropIntent.putExtra("outputX", 300); cropIntent.putExtra("outputY", 300); cropIntent.putExtra("return-data", false); // 建议设置为false,避免大图崩溃 cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri); cropIntent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); // 检查是否有能处理裁剪的应用 if (cropIntent.resolveActivity(getPackageManager()) != null) { startActivityForResult(cropIntent, REQUEST_CROP); } else { Toast.makeText(this, "没有找到支持裁剪的应用", Toast.LENGTH_SHORT).show(); } }注意事项
- 避免设置部分系统裁剪应用不支持的参数,比如有些系统不支持
outputFormat为PNG,建议用JPEG; - 不要设置
return-data=true,尤其是裁剪大图时,容易导致内存溢出,用EXTRA_OUTPUT指定输出文件更可靠; - 如果是从相册选择的图片Uri本身就是
content://格式(比如MediaStore返回的),直接使用即可,不需要转换。
- 避免设置部分系统裁剪应用不支持的参数,比如有些系统不支持
内容的提问来源于stack exchange,提问作者Ehsan.F

