最近做了一个关于应用升级的模块,网上查阅了各种资料,有静默安装,也有调用系统界面安装,由于静默安装需要root权限,所以我们应用还是通过intent安装。下面介绍一下在7.0一下和7.0以上的配置问题。注意红色内容。
第一步:
在AndroidManifest.xml清单文件中注册provider,因为provider也是Android四大组件之一,可以简单把它理解为向外提供数据的组件,这种组件在实际开发中用的频率并不高,四大组件都可以在清单文件中进行配置。
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.hty.remote.fileprovider" android:grantUriPermissions="true" android:exported="false"> <!--元数据--> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
注意:
exported
:要求必须为false,为true则会报安全异常。grantUriPermissions:true
,表示授予 URI 临时访问权限。authorities
组件标识,一般都以包名开头,避免和其它应用发生冲突。
第二步:指定共享的目录
上面配置文件中 android:resource="@xml/file_paths"
指的是当前组件引用 res/xml/file_paths.xml
这个文件。
我们需要在资源(res)目录下创建一个xml目录,然后创建一个名为“file_paths”(名字可以随便起,只要和在manifest注册的provider所引用的resource保持一致即可)的资源文件,内容如下:
<paths> <external-files-path name="path" path="Download"/> <root-path name="root" path="/" /> </paths>
name随便起,代表的就是sdcard的路径,一直到包名的那个文件夹下的files目录下的Download目录 ,这是我在项目中配置的paths,因为我把所有的配置都配完了之后还是会报FileUriExposedException,在网上找了很多资料才发现,要加一个root的path,然后问题就解决了。
注意:
files-path:
<paths>
<files-path name="path" path="Download"/>
</paths>
表示Context.getFilesDir()目录或者其子目录,示例 :/data/data/com.hty.remote/files/Download/
cache-path :
<paths>
<cache-path name="path" path="Download"/>
</paths>
表示Context.getCacheDir()目录或者其子目录,示例 : /data/data/com.hty.remote/cache/Download/
external-path:
<paths>
<external-path name="path" path="Download"/>
</paths>
表示Environment.getExternalStorageDirectory()目录或者其子目录,示例 : /storage/emulated/0/Download/
external-files-path:
<paths>
<external-files-path name="path" path="Download"/>
</paths>
表示Context.getExternalFilesDir(null)目录或者其子目录,示例 : /storage/emulated/0/Android/data/com.hty.remote/files/Download
external-cache-path:
<paths>
<external-cache-path name="path" path="Download"/>
</paths>
表示Context.getExternalCacheDir()目录或者其子目录,示例 : /storage/emulated/0/Android/data/com.hty.remote/cache/Download
第三步:使用FileProvider
public void installAPP(String path) { Log.d(TAG, "update app start " + path); File file = new File(path); Log.d(TAG, file.getPath()); if (file.exists()) { Intent i = new Intent(Intent.ACTION_VIEW); i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 7.0+以上版本 Log.d(TAG, context.getPackageName()); Uri apkUri = FileProvider.getUriForFile(context, "com.hty.remote.fileprovider", file); i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); i.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); i.setDataAndType(apkUri, "application/vnd.android.package-archive"); } else { i.setDataAndType(Uri.parse("file://" + file.toString()), "application/vnd.android.package-archive"); } context.startActivity(i); } else { Log.d(TAG, "file not exist"); }
以上为基本的调用方法,可以注意到的是,在7.0以上版本需要配置一个provider,才能访问uri,因为Android7.0引入“私有目录被限制访问”,“StrictMode API 政策”。
“私有目录被限制访问“ 是指在Android7.0中为了提高私有文件的安全性,面向 Android N 或更高版本的应用私有目录将被限制访问,” StrictMode API 政策” 是指禁止向你的应用外公开 file:// URI。 如果一项包含文件 file:// URI类型 的 Intent 离开你的应用,应用失败,并出现 FileUriExposedException 异常。
最后,8.0以上的版本还有一个新的问题,就是要在清单文件多加一个权限,否则你会发现调用没有任何反应,本人踩坑了。
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>