原文地址:http://www.czhzero.com/2016/12/21/how-to-install-apk-on-Android7-0/
Android系统升级到7.0之后,安全性提高了不少,过去我们通常是使用这样的代码进行apk的安装操作。
1 2 3 | Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); context.startActivity(intent); |
但是在Android7.0的系统上,运行这段代码,会报如下错误。
Caused by: android.os.FileUriExposedException
原因是,安卓官方为了提高私有文件的安全性,面向 Android 7.0 或更高版本的应用私有目录被限制访问 (0700)。此设置可防止私有文件的元数据泄漏,如它们的大小或存在性.
传递软件包网域外的 file:// URI 可能给接收器留下无法访问的路径。因此,尝试传递 file:// URI 会触发 FileUriExposedException。分享私有文件内容的推荐方法是使用 FileProvider。
1.定义一个FileProvider
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <manifest> ... <application> ... <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.mydomain.fileprovider" android:exported="false" android:grantUriPermissions="true"> ... </provider> ... </application> </manifest> |
2.添加可用权限的文件目录
在res目录下,增加xml文件夹,并新建一个名为 file_paths.xml
的文件。文件内容格式如下:
1 2 3 4 | <paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="name1" path="test1" /> ... </paths> |
标签下面必须包含至少包含以下标签中的一个或者多个。
files-path
1
| <files-path name="name1" path="test1" />
|
表示Context.getFilesDir()目录或者其子目录。
示例 : /data/data/com.chen.gradle/files/test1
cache-path
1
| <cache-path name="name2" path="test2" />
|
表示Context.getCacheDir()目录或者其子目录。
示例 : /data/data/com.chen.gradle/cache/test2
external-path
1
| <external-path name="name3" path="test3" />
|
表示Environment.getExternalStorageDirectory()目录或者其子目录。
示例 : /storage/emulated/0/test3
external-files-path
1
| <external-files-path name="name4" path="test4" />
|
表示Context.getExternalFilesDir(null)目录或者其子目录。
示例 : /storage/emulated/0/Android/data/com.chen.gradle/files/test4
external-cache-path
1
| <external-cache-path name="name5" path="test5" />
|
表示Context.getExternalCacheDir()目录或者其子目录。
示例 : /storage/emulated/0/Android/data/com.chen.gradle/cache/test5
3.增加到provider
通过<meta-data>
标签将上面的filepath添加到provider当中。
1 2 3 4 5 6 7 8 9 | <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.mydomain.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> |
4.通过provider生成Uri
1 2 3 4 | File imagePath = new File(Context.getFilesDir(), "test1"); File newFile = new File(imagePath, "default_image.jpg"); Uri contentUri = FileProvider.getUriForFile(getContext(), "com.mydomain.fileprovider", newFile); |
5.赋予临时权限给Uri
1
| intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
最终安装apk的代码变成这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public static void installApk(Context context, String apkPath) { if (context == null || TextUtils.isEmpty(apkPath)) { return; } File file = new File(apkPath); Intent intent = new Intent(Intent.ACTION_VIEW); //判读版本是否在7.0以上 if (Build.VERSION.SDK_INT >= 24) { //provider authorities Uri apkUri = FileProvider.getUriForFile(context, "com.mydomain.fileprovider", file); //Granting Temporary Permissions to a URI intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setDataAndType(apkUri, "application/vnd.android.package-archive"); } else { intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); } context.startActivity(intent); } |