Android N FileUriExposedException 适配

一. 背景

   Andriod N及以上版本中,一个应用提供自身文件给其它应用使用时,如果是file://格式的URI的话,会由StrictMode 触发FileUriExposedException 异常, 通常是长这样的:

05-21 17:12:07.438: E/AndroidRuntime(29373): FATAL EXCEPTION: main
05-21 17:12:07.438: E/AndroidRuntime(29373): Process: test.fileprovider, PID: 29373
05-21 17:12:07.438: E/AndroidRuntime(29373): android.os.FileUriExposedException: file:///storage/emulated/0/youbing.apk exposed beyond app through Intent.getData()
05-21 17:12:07.438: E/AndroidRuntime(29373):    at android.os.StrictMode.onFileUriExposed(StrictMode.java:1958)
05-21 17:12:07.438: E/AndroidRuntime(29373):    at android.net.Uri.checkFileUriExposed(Uri.java:2348)
05-21 17:12:07.438: E/AndroidRuntime(29373):    at android.content.Intent.prepareToLeaveProcess(Intent.java:9766)
05-21 17:12:07.438: E/AndroidRuntime(29373):    at android.content.Intent.prepareToLeaveProcess(Intent.java:9720)
05-21 17:12:07.438: E/AndroidRuntime(29373):    at android.app.Instrumentation.execStartActivity(Instrumentation.java:1614)
05-21 17:12:07.438: E/AndroidRuntime(29373):    at android.app.Activity.startActivityForResult(Activity.java:4508)
05-21 17:12:07.438: E/AndroidRuntime(29373):    at android.support.v4.app.BaseFragmentActivityApi16.startActivityForResult(BaseFrag

官方是建议我们使用Fileprovider来解决这种问题,当然我们也可以在应用内通过添加一个不设防的VmPolicy来规避。

二. FileProvider的方式

1. AndroidManifest中申明FileProvider
<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.test.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

android:authorities="com.test.fileprovider": 这个名称自己定义
android:exported="false":表明这个provider不用对外开放
android:grantUriPermissions="true": 这个一定要为true, 否则无法获得临时权限

2. res/xml中定义需要对外暴露的文件夹路径

<paths>里边的元素必须是一下的一个或者多个, 对要分享出去的文件,一定要声明 正确的路径,否则会报如下错误:

05-22 10:43:52.491: E/AndroidRuntime(19432): FATAL EXCEPTION: main
05-22 10:43:52.491: E/AndroidRuntime(19432): Process: test.fileprovider, PID: 19432
05-22 10:43:52.491: E/AndroidRuntime(19432): java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/youbing.apk
05-22 10:43:52.491: E/AndroidRuntime(19432):    at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:712)
05-22 10:43:52.491: E/AndroidRuntime(19432):    at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:401)
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_path" path="."/>
    <external-files-path name="external_files_path" path="."/>
    <external-cache-path name="external_cache_path" path="."/>
    <files-path name="files_path" path="."/>
    <cache-path name="cache_path" path="."/>
    <root-path name="root_path" path="."/>
</paths>

name:名称标志字符串,不可以同名!
path:文件夹“相对路径”,完整路径取决于当前的标签类型。

标签 对应目录
<files-path name="name" path="path" /> /data/data/pkgname/files/path/
<external-files-path name="name" path="path" /> /storage/emulated/0/Android/data/pkgname/files/path/
<external-cache-path name="name" path="path" /> /storage/emulated/0/Android/data/pkgname/cache/path/
<files-path name="name" path="path" /> /data/data/pkgname/files/path/
<cache-path name="name" path="path" /> /data/data/pkgname/cache/path/
<root-path name="name" path="path" /> /path/
3. getUriForFile生成content://类型的Uri
String str = "/youbing.apk";
String fileName = Environment.getExternalStorageDirectory().getAbsolutePath() + str;
File file = new File(fileName);
Uri uri = FileProvider.getUriForFile(MainActivity.this, "com.test.fileprovider", file);

通过FileProvider 提供的接口 getUriForFile(context, string , file)获取处理后的Uri, 参数二 就是在AndroidManifest.xml中声明的 android:authorities="com.test.fileprovider"的值

4. 给Uri授予临时权限,并使用Intent传递Uri
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivity(intent);

我这里的demo是从apk中尝试调起系统安装器安装 一个apk的场景,调用代码如下:

String str = "/youbing.apk";
String fileName = Environment.getExternalStorageDirectory().getAbsolutePath() + str;
File file = new File(fileName);
Uri uri = FileProvider.getUriForFile(MainActivity.this, "com.test.fileprovider", file);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri,"application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivity(intent);

三. VmPolicy 方式

这个很简单,通常就是在 Application的 oncreate方法中添加两句代码:

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

四. 遇到过的与FileProvider相关的问题

  1. 在写这个demo中遇到的,因为场景是调起系统安装器,按照上面两种方式处理后还是无法调起安装器,原因是我的 targetSdkVersion 是 26, 对应Android 8.0。因此需要在 AndroidManifest中声明权限,否则无法调起PackageInstaller.
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
  1. 王者荣耀自升级,安装时提示 安装包解析失败
    具体场景是 :进入王者荣耀后,下载完升级包,会自动拉起PackageInstaller进行安装,然后就弹出 安装包解析失败对话框,无法完成升级
    分析原因: 进入王者荣耀后,是横屏的,Android O上,下载完升级包拉起PackageInstaller的过程会依次进入 InstallStart - InstallStaging - PackageInstallerActivity 这几个activity,其中 InstallStart 和PackageInstallerActivity是指定强制竖屏,并且不随屏幕方向变化重走 activity 生命周期的
android:screenOrientation="portrait"    如果只指定强制竖屏,从横屏跳转过来时还是会走两次生命周期
android:configChanges="orientation|keyboardHidden|screenSize"

而InstallStaging重走了生命周期,onDestroy掉重新onCreate. 导致FileProvider授予的临时权限丢失,访问不到安装包文件URI,最终解析失败。
framework中临时权限的回收调用栈,从调用栈可以看到Uri临时访问权限确实随着目标activity的 destroy一起被移除掉了:

    05-22 15:52:03.848: E/john(17382):  at com.android.server.am.ActivityManagerService.removeUriPermissionIfNeededLocked(ActivityManagerService.java:10961)
    05-22 15:52:03.848: E/john(17382):  at com.android.server.am.UriPermissionOwner.removeUriPermissionLocked(UriPermissionOwner.java:95)
    05-22 15:52:03.848: E/john(17382):  at com.android.server.am.UriPermissionOwner.removeUriPermissionsLocked(UriPermissionOwner.java:69)
    05-22 15:52:03.848: E/john(17382):  at com.android.server.am.UriPermissionOwner.removeUriPermissionsLocked(UriPermissionOwner.java:64)
    05-22 15:52:03.848: E/john(17382):  at com.android.server.am.ActivityRecord.removeUriPermissionsLocked(ActivityRecord.java:1537)
    05-22 15:52:03.848: E/john(17382):  at com.android.server.am.ActivityStack.removeActivityFromHistoryLocked(ActivityStack.java:4119)
    05-22 15:52:03.848: E/john(17382):  at com.android.server.am.ActivityStack.activityDestroyedLocked(ActivityStack.java:4344)
    05-22 15:52:03.848: E/john(17382):  at com.android.server.am.ActivityManagerService.activityDestroyed(ActivityManagerService.java:9125)

解决办法: 当然就是在源码PackageInstaller的AndroidManifest.xml中给InstallStaging activity标签内添加

android:screenOrientation="portrait"   
android:configChanges="orientation|keyboardHidden|screenSize"

防止由activity重建导致的权限丢失

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容