解决 Android N 上报错:android.os.FileUriExposedException: file:///storage/emulated/0/

简介: 解决 Android N 上报错:android.os.FileUriExposedException: file:///storage/emulated/0/

解决android N文件访问crash android.os.FileUriExposedException file:///storage/emulated/0/xxx

原因:

Android N对访问文件权限收回,按照Android N的要求,若要在应用间共享文件,您应发送一项 content://URI,并授予 URI 临时访问权限。

而进行此授权的最简单方式是使用 FileProvider类。

解决方法:

1.在mainfest中加入FileProvider注册

<application>
 ......
     <provider
         android:authorities="你的应用名.fileprovider"
         android:name="android.support.v4.content.FileProvider"
         android:grantUriPermissions="true"
         android:exported="false">
         <meta-data
           android:name="android.support.FILE_PROVIDER_PATHS"
               android:resource="@xml/filepaths"/>
    </provider>
</application>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.配置filepaths文件

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path path="honjane/" name="files_path" />
</paths>
  • 1
  • 2
  • 3
  • 4

其中:

files-path代表的根目录: Context.getFilesDir()

external-path代表的根目录: Environment.getExternalStorageDirectory()

cache-path代表的根目录: getCacheDir()

<external-path path="honjane/" name="files_path" />
  • 1

path 代表要共享的目录

name 只是一个标示,随便取吧 自己看的懂就ok

举个栗子:通过provider获取到的uri链接

content://com.honjane.providerdemo.fileprovider/files_path/files/b7d4b092822da.pdf

name对应到链接中的files_path

path对应到链接中的 files ,当然files是在honjane/目录下

3.访问文件

 /**
     * 打开文件
     * 当手机中没有一个app可以打开file时会抛ActivityNotFoundException
     * @param context     activity
     * @param file        File
     * @param contentType 文件类型如:文本(text/html)     
     */
    public static void startActionFile(Context context, File file, String contentType) throws ActivityNotFoundException {
        if (context == null) {
            return;
        }
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        intent.setDataAndType(getUriForFile(context, file), contentType);
        if (!(context instanceof Activity)) {
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        context.startActivity(intent);
    }
    /**
     * 打开相机
     *
     * @param activity    Activity
     * @param file        File
     * @param requestCode result requestCode
     */
    public static void startActionCapture(Activity activity, File file, int requestCode) {
        if (activity == null) {
            return;
        }
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, getUriForFile(activity, file));
        activity.startActivityForResult(intent, requestCode);
    }
    private static Uri getUriForFile(Context context, File file) {
        if (context == null || file == null) {
            throw new NullPointerException();
        }
        Uri uri;
        if (Build.VERSION.SDK_INT >= 24) {
            uri = FileProvider.getUriForFile(context.getApplicationContext(), "你的应用名.fileprovider", file);
        } else {
            uri = Uri.fromFile(file);
        }
        return uri;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

同样访问相机相册都通过FileProvider.getUriForFile申请临时共享空间

已写成工具类上传到github,需要直接下载

使用方法简单,一行代码搞定

打开文件:

 try {
      FileUtils.startActionFile(this,path,mContentType);
    }catch (ActivityNotFoundException e){
 }
  • 1
  • 2
  • 3
  • 4
  • 5

调用相机:

 FileUtils.startActionCapture(this, file, requestCode);
  • 1

修复bug:

java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/xxx/xxx/file/12b31d2cab6ed.pdf

external与storage/emulated/0/对应,乍一看貌似没什么问题,path设置的是external的根路径,对应Environment.getExternalStorageDirectory(),

然而这个方法所获取的只是内置SD卡的路径,所以当选择的相册中的图片是外置SD卡的时候,就查找不到图片地址了,因此便抛出了failed to find configured root that contains的错误。

通过分析FileProvider源码发现,在xml解析到对应的标签后,会执行 buildPath() 方法来将根标签(files-path,cache-path,external-path等)对应的路径作为文件根路径,

在buildPath(),会根据一些常量判断是构建哪个目录下的path,除了上面介绍的几种path外还有个TAG_ROOT_PATH = “root-path” ,只有当不是root-path时才会去构建其他path,

官方也没介绍这个root-path,测试了一下发现对应的是DEVICE_ROOT指向的整个存储的根路径,这个bug就修复了

修改filepaths文件:

<paths>
      <root-path name="honjane" path="" />
</paths>


相关文章
|
3月前
|
关系型数据库 Unix Shell
File - os.tcsetpgrp(fd, pg)函数
`os.tcsetpgrp(fd, pg)` 函数在进行进程控制和信号管理时非常有用,但它涉及Unix底层的工作原理,因此使用时需具备相应知识,以确保正确和适用,并注意相关的权限和错误处理。
149 61
|
4月前
|
开发工具 Android开发
解决Android运行出现NDK at /Library/Android/sdk/ndk-bundle did not have a source.properties file
解决Android运行出现NDK at /Library/Android/sdk/ndk-bundle did not have a source.properties file
194 4
解决Android运行出现NDK at /Library/Android/sdk/ndk-bundle did not have a source.properties file
|
4月前
|
网络安全 图形学 Android开发
Unity与安卓丨AS报错:SSL peer shut down incorrectly
Unity与安卓丨AS报错:SSL peer shut down incorrectly
Unity与安卓丨AS报错:SSL peer shut down incorrectly
|
4月前
|
开发工具 图形学 Android开发
Unity与安卓丨unity报错:SDK Tools version 0.0 < 26.1.1
Unity与安卓丨unity报错:SDK Tools version 0.0 < 26.1.1
|
4月前
|
API Android开发 图形学
UNITY与安卓⭐三、安卓报错答疑合集
UNITY与安卓⭐三、安卓报错答疑合集
|
4月前
|
Java Android开发
解决Android编译报错:Unable to make field private final java.lang.String java.io.File.path accessible
解决Android编译报错:Unable to make field private final java.lang.String java.io.File.path accessible
663 1
|
4月前
|
Android开发
解決Android报错:Could not initialize class org.codehaus.groovy.reflection.ReflectionCache
解決Android报错:Could not initialize class org.codehaus.groovy.reflection.ReflectionCache
127 1
|
4月前
|
JavaScript Linux Windows
【应用服务 App Service】NodeJS +Egg 发布到App Service时遇见 [ERR_SYSTEM_ERROR]: A system error occurred:uv_os_get_passwd returned ENOENT(no such file or directory)
【应用服务 App Service】NodeJS +Egg 发布到App Service时遇见 [ERR_SYSTEM_ERROR]: A system error occurred:uv_os_get_passwd returned ENOENT(no such file or directory)
108 0
|
4月前
|
Android开发
解决Android、Flutter编译时Gradle报错:javax.net.ssl.SSLException: Connection reset
解决Android、Flutter编译时Gradle报错:javax.net.ssl.SSLException: Connection reset
498 0
|
6月前
|
搜索推荐 物联网 Linux
鸿蒙OS Next与安卓系统的比较
【6月更文挑战第2天】鸿蒙OS Next与安卓系统的比较
950 2