我们在开发应用程序时避免不了需要添加应用内升级功能。当应用程序启动时,如果检测到最新版本,将APK安装包从服务器下载下来,执行安装。
安装APK的代码一般写法如下,网上随处可以搜到
1. public static void installApk(Context context,File file){
2. Intent intent = new Intent(Intent.ACTION_VIEW);
3. Uri data = Uri.fromFile(file);
4. intent.setDataAndType(data, “application / vnd.android.package-archive” );
5. context.startActivity(意向);
6. }
然而,当我们在Android7.0手机中执行时,会发现报如下错误日志
1. 导致:android.os.FileUriExposedException:file:///storage/emulated/0/Android/data/net.csdn.blog.ruancoder/cache/test.apk通过Intent.getData()暴露在应用程序之外
2. 在android.os.StrictMode.onFileUriExposed(StrictMode.java:1799)
3. 在android.net.Uri.checkFileUriExposed(Uri.java:2346)
4. 在android.content.Intent.prepareToLeaveProcess(Intent.java:8933)
5. 在android.content.Intent.prepareToLeaveProcess(Intent.java:8894)
6. 在android.app.Instrumentation.execStartActivity(Instrumentation.java:1517)
7. 在android.app.Activity.startActivityForResult(Activity.java:4224)
8. 在android.support.v4.app.BaseFragmentActivityJB.startActivityForResult(BaseFragmentActivityJB.java:50)
9. 在android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:79)
10. 在android.app.Activity.startActivityForResult(Activity.java:4183)
11. 在android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:859)
12. 在android.app.Activity.startActivity(Activity.java:4507)
13. 在android.app.Activity.startActivity(Activity.java:4475)
我们来看一下FileUriExposedException官方文档
https://developer.android.google.cn/reference/android/os/FileUriExposedException.html
从Android 7.0开始,不再允许在app中把文件:// Uri暴露给其他应用程序,否则应用会抛出FileUriExposedException。原因在于,Google认为使用文件:// Uri存在一定的风险。比如,文件是私有的其他应用程序无法访问该文件或者其他应用程序没有申请READ_EXTERNAL_STORAGE运行时权限。解决方案是,使用FileProvider生成内容:// Uri来替代文件://
Uri。FileProvider官方文档:
: //developer.android .google.cn / reference / android / support / v4 / content / FileProvider.html
下面我们使用FileProvider解决上述异常
1.声明
FileProvider首先在清单文件中申明FileProvider。
1. < manifest >
2. ...
3. < application >
4. ...
5. < 提供者
6. android:name = “android.support.v4.content.FileProvider”
7. android:authority = “net.csdn.blog.ruancoder.fileprovider”
8. android:exported = “false”
9. android:grantUriPermissions = “true” >
10. < 元数据
11. android:name = “android.support.FILE_PROVIDER_PATHS”
12. android:resource = “@ xml / file_paths” />
13. </ provider >
14. ...
15. </ application >
16. </ manifest >
其他中文
android:name是固定写法
android:authority可自定义,是用来标识该提供者的唯一标识,建议结合包名来保证权威的唯一性
android:export必须设置成false,否则运行时会报错java .lang.SecurityException:不能导出提供程序
android:grantUriPermissions用于控制共享文件的访问权限。
<meta-data>节点中的android:resource指定了共享文件的路径。此处的file_paths即是该Provider对外提供文件的目录的配置文件,存放在res / xml /下
。2.添加file_paths.xml文件
文件格式如下
1. < 路径>
2. < files-path name = “name” path = “path” />
3. ...
4. </ paths >
其中中元素<path>是固定的,内部元素可以是以下节点:
<files-path name =“name”path =“path”/>对应getFilesDir()。
<cache-path name =“name”path =路径“/>对应getCacheDir()。
<external-path name =”name“path =”path“/>对应Environment.getExternalStorageDirectory()。
<external-files-path name =”name“path =”path“/>对应getExternalFilesDir()。
<external-cache-path name =“name”path =“path”/>对应getExternalCacheDir()。
此处,我们将下载的apk文件存放到sdcard中的Android / data / <package>缓存/下载中,file_paths.xml文件如下。
1. <?xml version = “1.0” encoding = “utf-8” ?>
2. < 路径>
3. < external-cache-path name = “cache_download” path = “download” />
4. </ paths >
3.在的Java代码中使用FileProvider
1. public static void installApk(Context context,File file){
2. Intent intent = new Intent(Intent.ACTION_VIEW);
3. 乌里数据
4. //判断版本大于等于7.0
5. if (Build.VERSION.SDK_INT> = Build.VERSION_CODES.N){
6. //“net.csdn.blog.ruancoder.fileprovider”即是在清单文件中配置的权限
7. data = FileProvider.getUriForFile(context, “net.csdn.blog.ruancoder.fileprovider” ,file);
8. //给目标应用一个临时授权
9. intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
10. } else {
11. data = Uri.fromFile(file);
12. }
13. intent.setDataAndType(data, “application / vnd.android.package-archive” );
14. context.startActivity(意向);
15. }