Better

业精于勤荒于嬉

N文件共享之android.os.FileUriExposedException

Better's Avatar 2016-12-30 Android-Error

  1. 1. 添加Provider
  2. 2. 添加分享文件路径信息
  3. 3. 适配android N
  4. 4. 参考

在Android N上直接分享文件会报错,比如调起安装 APK 文件:

1
android.os.FileUriExposedException: file:///storage/72AD-2013/ELTP/apk/ELTP.apk exposed beyond app through Intent.getData()

由于Android N出于安全考虑,禁止用户直接共享文件,必须以provider的方式来共享文件。
即:

  • 禁止以file://URI公开你的文件的方式共享文件。
  • 以content://URI并授予URI临时访问权限的方式来访问

解决方法是使用FileProvider来规避,安装一下三个步凑来添加

添加Provider


注意:
authorities:app的包名
grantUriPermissions:必须是true,表示授予 URI 临时访问权限
exported:false,不导出给其他APP用
resource:中的@xml/file_paths是我们接下来要添加的文件

添加分享文件路径信息

path可以选择的路径有:

tag 对应的目录路径
<files-path name=”name” path=”path”/> Context.getFilesDir()
<external-path name=”name” path=”path”/> Environment.getExternalStorageDirectory()
<cache-path name=”name” path=”path”/> Context.getCacheDir()
<external-files-path name=”name” path=”path”/> Context#getExternalFilesDir(String)
<external-cache-path/ name=”name” path=”path”> Context.getExternalCacheDir()
<external-media-path name=”name” path=”path” /> Context.getExternalMediaDirs()
  • name:就是你给这个访问路径起个名字,只是显示用随便取
  • path:需要临时授权访问的路径名称(.代表当前路径下所有路径)

比如-来源

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
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">

<!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/DCIM/camerademo目录-->
<external-path name="DCIM" path="DCIM/camerademo" />

<!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/目录-->
<external-path path="." name="external_storage_root" />

<!--代表外部存储区域的根目录下的文件 Environment.getExternalStorageDirectory()/Pictures/camerademo目录-->
<external-path name="Pictures" path="Pictures/camerademo" />

<!--代表app 私有的存储区域 Context.getFilesDir()目录下的images目录 /data/user/0/com.hm.camerademo/files/images-->
<files-path name="private_files" path="images" />

<!--代表app 私有的存储区域 Context.getCacheDir()目录下的images目录 /data/user/0/com.hm.camerademo/cache/images-->
<cache-path name="private_cache" path="images" />

<!--代表app 外部存储区域根目录下的文件 Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)目录下的Pictures目录-->
<!--/storage/emulated/0/Android/data/com.xx.xxxxxx/files/Pictures-->
<external-files-path name="external_files" path="Pictures" />

<!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的images目录-->
<!--/storage/emulated/0/Android/data/com.xx.xxxxxx/cache/images-->
<external-cache-path name="external_cache" path="" />
</paths>

适配android N

涉及到文件分享的时候,做下适配,使用Fileprovider来生成Uri

1
2
3
4
5
6
7
8
9
10
11
12
private void installFile() {
File f = new File(updateFilePath);
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Utils.isBelowAndroidVersion(Build.VERSION_CODES.N)){
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.fromFile(f), "application/vnd.android.package-archive");
} else {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(FileProvider.getUriForFile(context,"com.yzzy.elt.passenger.fileprovider",f),"application/vnd.android.package-archive");
}
context.startActivityForResult(intent, C.REQUST_CODE_UPDATE);
}

打开apk:application/vnd.android.package-archive
打开图片:image/*

最后需要将对应的Url添加访问权限
添加flag,该方式主要用于针对intent.setData,setDataAndType以及setClipData相关方式传递uri的

1
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

或者找出匹配的Intent使用函数来

  • 授权 grantUriPermission(String toPackage, Uri uri, int modeFlags)
  • 移除权限 revokeUriPermission(Uri uri, int modeFlags);
1
2
3
4
5
6
List<ResolveInfo> resInfoList = context.getPackageManager()
.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName, uri, flag);
}

参考

developer-FileProvider

This article was last updated on days ago, and the information described in the article may have changed.