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

Android Uri写入权限遭拒:java.lang.SecurityException异常如何处理?

解决Android修改Exif时的SecurityException权限问题

嘿,我帮你分析下这个问题——你遇到的java.lang.SecurityException其实不是因为没加对权限,而是两个关键误区导致的:MANAGE_DOCUMENTS权限的特殊限制,还有你调用grantUriPermission()的方式完全错了。

先搞清楚核心问题

  • MANAGE_DOCUMENTS权限没用:这个权限是系统级权限,只有系统预装的文件管理器或者特定系统应用才能申请到,普通APP就算在Manifest里声明了,系统也不会授予,所以这个权限可以直接删掉,它解决不了你的问题。
  • grantUriPermission()用错了:你传的第一个参数是getApplicationContext().getPackageCodePath(),这是你APK的本地路径,但这个方法需要的是目标应用的包名。而且grantUriPermission()是用来给其他APP授权访问Uri的,不是给自己APP用的!给自己APP授权应该用takePersistableUriPermission()来获取持久访问权限。

正确的解决方案步骤

1. 确保你是通过SAF获取的Uri

如果你的AddArray里的Uri是自己构造的file://类型,在Android 10+是绝对无法直接写入的,必须通过**Storage Access Framework(SAF)**让用户选择文件,这样拿到的content://类型Uri才支持持久权限。

2. 在获取Uri时先拿到持久权限

当用户通过SAF选择文件后,你需要立即调用takePersistableUriPermission()保存权限,这样后续才能随时访问这个文件:

// 假设你在ActivityResultCallback中拿到用户选择的Uri
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == YOUR_PICK_FILE_REQUEST_CODE && resultCode == RESULT_OK) {
        Uri selectedUri = data.getData();
        // 申请持久的读写权限
        int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
        getContentResolver().takePersistableUriPermission(selectedUri, flags);
        // 把这个Uri保存到你的AddArray里
    }
}

3. 修正你的Exif修改代码

去掉错误的grantUriPermission()调用,直接用已获取的持久权限操作文件:

Uri targetUri = (Uri) AddArray[i];
ParcelFileDescriptor parcelFileDescriptor = null;
try {
    // 先检查是否有持久写入权限(可选,但能避免异常)
    boolean hasWritePermission = getContentResolver().getPersistedUriPermissions().stream()
            .anyMatch(perm -> perm.getUri().equals(targetUri) 
                    && (perm.getFlags() & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0);

    if (!hasWritePermission) {
        Toast.makeText(getApplicationContext(), "缺少文件写入权限,请重新选择文件", Toast.LENGTH_SHORT).show();
        return;
    }

    // 打开文件写入流
    parcelFileDescriptor = getContentResolver().openFileDescriptor(targetUri, "w");
    if (parcelFileDescriptor == null) {
        Toast.makeText(getApplicationContext(), "无法打开文件", Toast.LENGTH_SHORT).show();
        return;
    }

    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    ExifInterface ei = new ExifInterface(fileDescriptor);
    
    // 设置GPS信息
    ei.setAttribute(TAG_GPS_LATITUDE, "33/1,35/1,4091/100");
    ei.setAttribute(TAG_GPS_LATITUDE_REF, "N");
    ei.setAttribute(TAG_GPS_LONGITUDE, "73/1,1/1,4390/100");
    ei.setAttribute(TAG_GPS_LONGITUDE_REF, "E");
    
    ei.saveAttributes();
    Toast.makeText(getApplicationContext(), "GPS信息修改成功", Toast.LENGTH_SHORT).show();

} catch (Exception e) {
    Toast.makeText(getApplicationContext(), "修改失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
    e.printStackTrace();
} finally {
    // 务必关闭流,避免资源泄漏
    if (parcelFileDescriptor != null) {
        try {
            parcelFileDescriptor.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

4. 额外优化建议

如果是处理媒体库中的图片,也可以考虑用MediaStore API直接修改Exif,不需要依赖SAF Uri:

ContentValues values = new ContentValues();
// 直接设置GPS相关的Exif字段
values.put(MediaStore.Images.Media.GPS_LATITUDE, 33.354091);
values.put(MediaStore.Images.Media.GPS_LATITUDE_REF, "N");
values.put(MediaStore.Images.Media.GPS_LONGITUDE, 73.014390);
values.put(MediaStore.Images.Media.GPS_LONGITUDE_REF, "E");

// 更新媒体库中的文件
getContentResolver().update(targetMediaUri, values, null, null);

这样处理更简洁,而且不需要额外申请持久权限,适合批量修改媒体文件的场景。

内容的提问来源于stack exchange,提问作者Abdullah Mufti

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:08:35