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

切换至getExternalFilesDir()后Android相机功能失效问题

Android 12(API 31)切换存储路径后相机崩溃及图片无法访问问题解决

问题背景

在Android 12.0(API 31)环境下,将拍照存储路径从Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)切换为官方推荐的getExternalFilesDir(Environment.DIRECTORY_PICTURES)后,出现相机应用停止运行、拍摄的图片无法访问的问题,相关核心代码如下:

创建图片文件代码

private File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = "JPEG_" + timeStamp + "_";
    File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
    //File storageDir = getExternalFilesDir(null).getAbsoluteFile();

    File imageFile = File.createTempFile(
            imageFileName,  /* prefix */
            ".jpg",         /* suffix */
            storageDir      /* directory */
    );

    System.out.print(storageDir);

    return imageFile;
}

相机结果回调代码

public void onActivityResult(int requestCode, int resultCode, Intent data) {

    if (requestCode != INPUT_FILE_REQUEST_CODE || mUploadMessage == null) {
        super.onActivityResult(requestCode, resultCode, data);
        return;
    }
    try {
        String file_path = mCameraPhotoPath.replace("file:","");
        File file = new File(file_path);
        size = file.length();
        Uri x =data.getData();

    }catch (Exception e){
        Log.e("Error!", "Error while opening image file" + e.getLocalizedMessage());
    }

    if (data != null || mCameraPhotoPath != null) {
        Integer count = 0;
        ClipData images = null;
        try {
            images = data.getClipData();
        }catch (Exception e) {
            Log.e("Error!", e.getLocalizedMessage());
        }

        if (images == null && data != null && data.getDataString() != null) {
            count = data.getDataString().length();
        } else if (images != null) {
            count = images.getItemCount();
        }
        Uri[] results = new Uri[count];
        // Check that the response is a good one
        if (resultCode == Activity.RESULT_OK) {
            if (size != 0) {
                // If there is not data, then we may have taken a photo
                if (mCameraPhotoPath != null) {
                    results = new Uri[]{Uri.parse(mCameraPhotoPath)};
                }
            } else if (data.getClipData() == null) {
                results = new Uri[]{Uri.parse(data.getDataString())};
            } else {

                for (int i = 0; i < images.getItemCount(); i++) {
                    results[i] = images.getItemAt(i).getUri();
                }
            }
        }

        mUploadMessage.onReceiveValue(results);
        mUploadMessage = null;
    }
}

问题原因

  1. 私有目录权限限制:getExternalFilesDir()返回的是应用专属的私有存储目录,其他应用(包括系统相机)没有写入权限,直接传递file://Uri会导致相机因权限不足崩溃。
  2. Uri格式不兼容:Android 7.0(API 24)及以上禁止跨应用传递file://Uri,必须使用content://格式的Uri,通过FileProvider授权其他应用访问文件。

修复方案

1. 配置FileProvider

在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_pictures" path="Pictures" />
</paths>

这里path="Pictures"对应getExternalFilesDir(Environment.DIRECTORY_PICTURES)的目录。

2. 修改图片文件创建逻辑

更新createImageFile()方法,使用应用私有目录并保存文件路径:

private File createImageFile() throws IOException {
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
    String imageFileName = "JPEG_" + timeStamp + "_";
    // 使用应用私有Pictures目录
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    File imageFile = File.createTempFile(
            imageFileName,
            ".jpg",
            storageDir
    );
    // 保存图片路径(用于后续访问)
    mCameraPhotoPath = imageFile.getAbsolutePath();
    return imageFile;
}

3. 调整相机启动逻辑

启动相机时,通过FileProvider生成content://Uri,并授予相机临时写入权限:

private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        File photoFile = null;
        try {
            photoFile = createImageFile();
        } catch (IOException ex) {
            // 处理文件创建异常
        }
        if (photoFile != null) {
            Uri photoUri = FileProvider.getUriForFile(this,
                    BuildConfig.APPLICATION_ID + ".fileprovider",
                    photoFile);
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
            // 授予相机临时写入权限
            takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            startActivityForResult(takePictureIntent, INPUT_FILE_REQUEST_CODE);
        }
    }
}

4. 修改onActivityResult处理逻辑

相机拍照完成后,直接通过保存的文件路径访问图片(使用EXTRA_OUTPUT时data会为空):

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode != INPUT_FILE_REQUEST_CODE || mUploadMessage == null) {
        return;
    }

    if (resultCode == Activity.RESULT_OK) {
        Uri[] results = null;
        if (mCameraPhotoPath != null) {
            File photoFile = new File(mCameraPhotoPath);
            if (photoFile.exists()) {
                // 生成content://Uri用于后续操作
                Uri photoUri = FileProvider.getUriForFile(this,
                        BuildConfig.APPLICATION_ID + ".fileprovider",
                        photoFile);
                results = new Uri[]{photoUri};
            }
        }
        mUploadMessage.onReceiveValue(results);
        mUploadMessage = null;
    } else {
        mUploadMessage.onReceiveValue(null);
        mUploadMessage = null;
    }
}

关键注意事项

  • Android 10(API 29)及以上无需申请WRITE_EXTERNAL_STORAGE权限,应用私有目录访问无需额外权限。
  • 确保file_paths.xml中的路径与getExternalFilesDir()参数对应,避免路径匹配失败。
  • 启动相机时必须添加Intent.FLAG_GRANT_WRITE_URI_PERMISSION,否则相机无法写入文件。

内容的提问来源于stack exchange,提问作者Brian Kunak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 06:45:33