Android 6调用相机拍照并显示图片异常问题
嘿,针对你遇到的Android 6上调用系统相机拍照后,在ActivityThread.performResumeActivity抛出RuntimeException的问题,我整理了几个最可能的原因和对应的解决办法:
1. 动态权限未正确申请(最常见原因)
Android 6.0(API 23)开始引入了动态权限机制,CAMERA和WRITE_EXTERNAL_STORAGE都属于危险权限,只在AndroidManifest.xml中声明是不够的,必须在代码中主动申请并获得用户授权。如果权限缺失,相机操作会异常,进而在Activity恢复时触发崩溃。
解决步骤:
- 先在Manifest中声明基础权限:
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-feature android:name="android.hardware.camera" android:required="true" />
- 在调用相机Intent前,检查并申请权限:
private static final int REQUEST_PERMISSIONS = 100; // 检查权限是否已授权 private boolean checkPermissions() { int cameraPerm = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA); int storagePerm = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); return cameraPerm == PackageManager.PERMISSION_GRANTED && storagePerm == PackageManager.PERMISSION_GRANTED; } // 发起权限申请 private void requestPermissions() { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSIONS); } // 处理权限申请结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_PERMISSIONS) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 权限通过,启动相机 launchCameraIntent(); } else { Toast.makeText(this, "需要相机和存储权限才能完成拍照", Toast.LENGTH_SHORT).show(); } } }
记得只有在权限通过后,再启动相机Intent。
2. 文件存储Uri的权限问题(适配FileProvider)
虽然Android 7.0才强制要求用content:// Uri替代file:// Uri,但部分Android 6的定制ROM也会限制第三方应用访问file:// Uri指向的文件,导致相机无法写入你指定的图片文件,最终在Activity恢复时引发异常。
解决步骤:
- 在
AndroidManifest.xml中添加FileProvider声明:
<application ...> <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> </application>
- 在
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/" /> </paths>
- 生成Content Uri并启动相机Intent:
private File createImageFile() throws IOException { // 创建临时图片文件 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); return File.createTempFile(imageFileName, ".jpg", storageDir); } private void launchCameraIntent() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getPackageManager()) != null) { File photoFile = null; try { photoFile = createImageFile(); } catch (IOException ex) { ex.printStackTrace(); } if (photoFile != null) { Uri photoURI = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", photoFile); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); // 授予相机应用写入权限 takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); } } }
3. Bitmap加载/缩放导致的内存溢出(OOM)
如果拍照后的原图尺寸过大,直接加载为Bitmap可能会导致内存溢出,进而在Activity Resume时触发RuntimeException。
解决步骤:
加载图片时用BitmapFactory.Options进行采样缩放,避免加载完整尺寸的图片:
private Bitmap getScaledBitmap(String filePath, int targetWidth, int targetHeight) { BitmapFactory.Options options = new BitmapFactory.Options(); // 先只获取图片尺寸,不加载Bitmap到内存 options.inJustDecodeBounds = true; BitmapFactory.decodeFile(filePath, options); // 计算合适的采样率 options.inSampleSize = calculateInSampleSize(options, targetWidth, targetHeight); // 加载缩放后的Bitmap options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(filePath, options); } private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // 找到最大的采样率,保证缩放后图片不小于目标尺寸 while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; }
在onActivityResult中拿到图片路径后,调用上述方法加载缩放后的Bitmap,再设置到ImageView中。
你可以先从动态权限检查入手,这是Android 6上相机相关崩溃最常见的原因,再逐步排查Uri适配和Bitmap加载的问题。
内容的提问来源于stack exchange,提问作者Thanos Infosec

