Android 8.0一加3拍照应用崩溃,Android 6.0模拟器正常求助
解决Android 8.0设备拍照崩溃问题(兼容Android 6.0+)
看起来你遇到的是Android版本兼容性问题,尤其是Android 7.0+的文件权限限制和相机返回数据的不确定性导致的崩溃,我来帮你一步步解决:
1. 核心问题分析
你的代码在Android 6.0模拟器正常,但在Android 8.0真机崩溃,主要有两个原因:
- Android 7.0+的FileUriExposedException:系统禁止应用直接向其他应用暴露
file://格式的Uri,否则会抛出权限异常; - 相机返回数据不可靠:部分相机应用(尤其是高版本系统的)不会将缩略图放入
data.getExtras()中,甚至data本身可能为null,直接调用会触发空指针异常。
2. 解决方案:改用FileProvider指定拍照输出路径
我们需要通过FileProvider生成安全的Uri,让相机应用将照片保存到我们指定的位置,同时避免依赖不可靠的返回数据。
步骤1:注册FileProvider到Manifest
在AndroidManifest.xml的<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>
步骤2:创建文件路径配置文件
在res/xml目录下新建file_paths.xml(如果没有xml目录就创建一个),内容如下:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 指定应用外部存储的Pictures目录作为照片保存路径 --> <external-files-path name="captured_images" path="Pictures" /> </paths>
步骤3:修改相机Intent启动代码
替换原来的拍照Intent代码,改为指定输出文件的方式:
private File mCapturedPhotoFile; // 保存拍照后的文件引用 private void startCamera() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 先确认有相机应用能处理这个Intent if (takePictureIntent.resolveActivity(getPackageManager()) == null) { Toast.makeText(this, "未找到可用相机应用", Toast.LENGTH_SHORT).show(); return; } try { // 创建临时照片文件 mCapturedPhotoFile = createTempPhotoFile(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "创建照片文件失败", Toast.LENGTH_SHORT).show(); return; } // 生成安全的FileProvider Uri Uri photoUri = FileProvider.getUriForFile( this, BuildConfig.APPLICATION_ID + ".fileprovider", // 和Manifest中的authorities一致 mCapturedPhotoFile ); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); startActivityForResult(takePictureIntent, TAKE_IMAGE_REQUEST); } // 创建临时照片文件的工具方法 private File createTempPhotoFile() throws IOException { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); String fileName = "CAPTURE_" + timeStamp + "_"; File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); return File.createTempFile(fileName, ".jpg", storageDir); }
步骤4:重构onActivityResult逻辑
不再依赖返回的data,直接使用我们创建的mCapturedPhotoFile路径:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == TAKE_IMAGE_REQUEST && resultCode == RESULT_OK) { if (mCapturedPhotoFile == null || !mCapturedPhotoFile.exists()) { Toast.makeText(this, "照片获取失败", Toast.LENGTH_SHORT).show(); return; } // 直接使用照片文件的绝对路径 String photoPath = mCapturedPhotoFile.getAbsolutePath(); try { Img img = crearImg(photoPath); if (user.isOnline()) { Img.uploadImage(NoteActivity.this, img); } } catch (Exception e) { e.printStackTrace(); // 建议打印异常,方便排查问题 Toast.makeText(this, "处理照片失败", Toast.LENGTH_SHORT).show(); } } }
3. 补充:动态权限申请
如果你的应用targetSdkVersion >= 23,还需要动态申请相机和存储权限:
private static final int REQUEST_CAMERA_PERMISSIONS = 101; // 在启动相机前先检查权限 private void checkPermissionsThenStartCamera() { String[] requiredPermissions = { Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE }; boolean allGranted = true; for (String permission : requiredPermissions) { if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (allGranted) { startCamera(); } else { ActivityCompat.requestPermissions(this, requiredPermissions, REQUEST_CAMERA_PERMISSIONS); } } // 处理权限申请结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_CAMERA_PERMISSIONS) { boolean allGranted = true; for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (allGranted) { startCamera(); } else { Toast.makeText(this, "需要相机和存储权限才能拍照", Toast.LENGTH_SHORT).show(); } } }
最后,记得把原来的启动相机代码替换成调用checkPermissionsThenStartCamera(),而不是直接启动Intent。
这样修改后,既解决了Android 8.0的权限问题,又避免了依赖不可靠的相机返回数据,应该能解决你的崩溃问题。
内容的提问来源于stack exchange,提问作者Artur Alcoverro Pastó




