You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android API29及以上调用相机拍摄存储崩溃问题求助

解决Android 9+相机调用无报错崩溃问题

我之前踩过几乎一模一样的坑!这种没有错误栈的崩溃,大概率是FileProvider配置或者URI权限处理在Android P(9.0)及以上的安全限制下出了问题,咱们一步步来修复:

1. 先检查FileProvider的清单配置是否完全正确

这是最容易忽略的点,确保清单里的<provider>标签满足以下要求:

  • authorities必须和你代码里getPackageName() + ".fileprovider"完全一致
  • 必须添加android:exported="false"和android:grantUriPermissions="true"(这两个属性在Android 9+是硬性要求,尤其是grantUriPermissions,否则相机APP无法访问你提供的URI)

示例配置代码:

<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. 确认file_paths.xml的路径配置匹配你的缓存目录

你的图片存在getExternalCacheDir()下的camera文件夹,所以file_paths.xml里必须正确配置external-cache-path,路径要完全对应:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- name可以自定义,path必须和你代码里的"camera"文件夹一致 -->
    <external-cache-path name="camera_images" path="camera/" />
</paths>

如果这里的路径不匹配,FileProvider会生成无效的URI,直接导致崩溃且无报错栈。

3. 给相机APP明确授予URI的读写权限(关键修复点)

Android 9+收紧了URI权限的管控,即使你用了FileProvider,也需要主动给相机APP授予读写权限。你可以在启动相机前添加这两行:

takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

或者更稳妥地,用grantUriPermission明确授予:

PackageManager pm = getPackageManager();
if (takePictureIntent.resolveActivity(pm) != null) {
    String cameraPackage = takePictureIntent.resolveActivity(pm).getPackageName();
    grantUriPermission(cameraPackage, getCacheImagePath(fileName), 
        Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
    startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}

4. 给getCacheImagePath添加异常捕获和日志

无报错栈的崩溃往往是因为异常被系统吞噬了,给你的路径生成方法加上异常捕获,就能定位具体问题:

private Uri getCacheImagePath(String fileName) {
    try {
        File path = new File(getExternalCacheDir(), "camera");
        if (!path.exists()) {
            boolean isCreated = path.mkdirs();
            if (!isCreated) {
                Log.e("ImagePicker", "Failed to create camera cache directory");
                return null;
            }
        }
        File imageFile = new File(path, fileName);
        // 确保用AndroidX的FileProvider,避免老support库的兼容性问题
        return FileProvider.getUriForFile(ImagePickerActivity.this, 
            getPackageName() + ".fileprovider", imageFile);
    } catch (Exception e) {
        Log.e("ImagePicker", "Error generating cache image URI", e);
        return null;
    }
}

这样如果路径创建失败或者FileProvider生成URI出错,你就能在Logcat里看到具体的异常信息了。

5. (可选)替换startActivityForResult为registerForActivityResult

startActivityForResult在AndroidX中已经被废弃,虽然Android 9还能使用,但换成新的API可以避免一些隐性的兼容性问题:

// 先定义ActivityResultLauncher
private ActivityResultLauncher<Intent> takePictureLauncher = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        result -> {
            if (result.getResultCode() == RESULT_OK) {
                // 处理拍照成功的逻辑
                Uri imageUri = getCacheImagePath(fileName);
                // ...
            }
        }
);

// 调用相机的代码替换为:
fileName = System.currentTimeMillis() + ".jpg";
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Uri imageUri = getCacheImagePath(fileName);
if (imageUri == null) {
    Toast.makeText(this, "Failed to create image path", Toast.LENGTH_SHORT).show();
    return;
}
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
    takePictureLauncher.launch(takePictureIntent);
}

内容的提问来源于stack exchange,提问作者Navid Abutorab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 00:42:47