在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可以选择的路径有:
- 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