一、高低版本兼容代码及配置
高版本提高了sdcard、 app文件空间的访问权限,高低版本的系统api有一定区别,Android7.0 及以上,开放(暴露)私有数据文件的唯一方式是通过 ContentProvider 来实现(我们的app提供我们的文件给系统安装程序)。
基本步骤
1、在AndroidManifest.xml 中配置 ContentProvider 信息;
2、配置要开放的 paths 信息;
3、Java 代码中通过 FileProvider 封装文件信息。
1.java代码
private void install(String path) {
File apk = new File(path);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//注意第二个参数,要保持和manifest中android:authorities的值相同
Uri uri = FileProvider.getUriForFile(context,
context.getPackageName() + ".fileProvider", apk);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(apk), "application/vnd.android.package-archive");
}
try {
context.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
特别注意
Uri uri = FileProvider.getUriForFile(context, context.getPackageName() + “.fileProvider”
, apk);
第二个参数一定要和provider 的android:authorities="${applicationId}.fileProvider"匹配。
applicationId是包名,context.getPackageName()
也是包名,所以
context.getPackageName() + “.fileProvider”
和 ${applicationId}.fileProvider
是相同的字符串
2.AndroidManifest.xml 配置ContentProvider
配置安装权限
<!-- 应用安装权限 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
配置ContentProvider
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider" />
</provider>
provider 属性说明:
-
android:name
属性填写的是 FileProvider 类的完整名称。这个类可以填写两个值,一个是位于 support(android.support.v4.content.FileProvider) 包下的,另一个是位于 androidx(androidx.core.content.FileProvider) 包下的。这两种都可以填写,本质上没有区别。要根据实际情况来决定用哪个,即要看工程引的是 androidx 支援包还是 support 支援包。 -
android:authorities
属性就是和普通的 ContentProvider 一样的用于访问文件资源的 uri 标签头。值内容根据实际需要来填写即可。 -
android:exported
与android:grantUriPermissions
两个属性的值照着填就好。大致是指允许其它应用单次使用自己的 Provider 资源。 -
meta-data
标签中的内容需要关注的是android:resource
属性中的内容。这个属性的值引向一个 xml目录的文件,这份 xml 文件描述要给其他应用访问的文件目录。
3.paths xml 配置
在工程 res 目录下新建一个 xml 没有的话新建)目录,则在目录中创建一个file_provider.xml
(名称随意,和 android:resource=“@xml/file_provider
” 一致即可),并在xml中添加需要对外提供的目录路径
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="path1"
path="/data/dir1/" />
<external-path
name="path2"
path="/" />
<external-files-path
name="path3"
path="/data/dir2" />
<external-cache-path
name="path4"
path="/data/dir3" />
<cache-path
name="path5"
path="/data" />
<files-path
name="path6"
path="/ff" />
</paths>
简要说明:
不带external 是程序安装目录下的目录,cache-path 指程序安装目录下的cache目录。
带有external 的是指sdcard 目录下对应包名的专属目录,例如external-cache-path 指sdcard 目录下对应包名的专属cache目录。
例如下图是斗鱼直播的files目录:
<external-files-path
name="path3"
path="/" />
对应下图
如果还有子目录,对应的增加接口,例如files 目录下的douyu, 那么xml如下:
<external-files-path
name="path3"
path="/douyu/" />
二、老版本代码
老版本代码相对简单,粗暴且实用,只要知道 apk 的path,并拥有访问权限(sdcard),
再加上安装权限即可。
File apk = new File(apkPath);
Uri uri = Uri.fromFile(apk);
Intent intent = new Intent();
intent.setClassName("com.android.packageinstaller",
"com.android.packageinstaller.PackageInstallerActivity");
intent.setData(uri);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
manifest 添加对应的权限
<!--sdcard 读权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
三、可能出的问题及处理
- java.lang.NullPointerException: Attempt to invoke virtual method ‘android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)’ on a null object reference
at android.support.v4.content.FileProvider.parsePathStrategy(FileProvider.java:591)
原因:FileProvider.getUriForFile(context,context.getPackageName() + “.fileProvider”
, apk); 第二个参数 “包名.fileProvider” 和 provider.xml 中 android:authorities=“${applicationId}.fileProvider
” 的值(具体值:如com.eagle.app.fileProvider)不同,请仔细核对和修改一致。 - 权限问题
一般app需要安装权限,除了在清单文件中列出外,还需要手动确认,注意留意并确认 - 下载的安装文件路径与file_provider 中描述的文件不匹配,仔细核对路径与描述。
- 安全提示要选择允许