应用中对APK进行安装
权限
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> //允许安装未知来源的app
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
说明:REQUEST_INSTALL_PACKAGES该权限是8.0及以后得版本产生的,低于此版本不需要动态申请权限(当你动态申请权限的时候,会得到该权限没有授权的回调,但是你去申请权限是弹不出选择窗口,而是直接进了未授权的回调)。
清单文件
<application>
................
<!--适配7.0以上安装app-->
<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/filepaths" /> //说明:这里的filepaths是res下面新建的xml名称的文件夹下的文件
</provider>
</application>
res资源文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<external-path path="." name="download" />
</paths>
</resources>
该文件 <paths>标签下的标签有如下几种类型:
1、<files-path path="" name="camera_photos" />
该方式提供在应用的内部存储区的文件/子目录的文件。它对应Context.getFilesDir返回的路径: eg:"/data/data/com.jph.simple/files"。
2、<cache-path name="name" path="path" />
该方式提供在应用的内部存储区的缓存子目录的文件。它对应getCacheDir返回的路径:eg:“/data/data/com.jph.simple/cache”;
3、<external-path name="name" path="path" />
该方式提供在外部存储区域根目录下的文件。它对应Environment.getExternalStorageDirectory返回的路径:eg:"/storage/emulated/0";
4、<external-files-path name="name" path="path" />
该方式提供在应用的外部存储区根目录的下的文件。它对应Context#getExternalFilesDir(String) Context.getExternalFilesDir(null)返回的路径。eg:"/storage/emulated/0/Android/data/com.jph.simple/files"。
5、<external-cache-path name="name" path="path" />
该方式提供在应用的外部缓存区根目录的文件。它对应Context.getExternalCacheDir()返回的路径。eg:"/storage/emulated/0/Android/data/com.jph.simple/cache"。
6、<root-path path="" name="camera_photos" />
root-path
代表/
也就是Android设备的根目录,该目录下包含着手机内部存储器,外置SD卡等所有文件的目录。
综上:对filepaths,xml文件的编写,需要根据你的apk包所在文件路径来选择paths的相对应类型,
从而保障调用FileProvider.getUriForFile()时不会报“Failed to find configured root that contains”异常。
下载
private void loadFromServer() {
DialogUtil.getInstance().showDialogText(this,"0%");
OkGo.<File>get(appurl)
.tag(this)
.execute(new FileCallback() {
@Override
public void onSuccess(Response<File> response) {
dialogDismiss();
installApp(response.body());
}
@Override
public void onError(Response<File> response) {
super.onError(response);
if (newVersionBean!=null&&newVersionBean.getData()!=null&&"1".equals(newVersionBean.getData().getIs_force_update())){
android.os.Process.killProcess(android.os.Process.myPid()); //获取PID
System.exit(0);
}else{
dialogDismiss();
displayMessage(getResources().getString(R.string.prompt_downloadfauil));
}
}
@Override
public void downloadProgress(Progress progress) {
super.downloadProgress(progress);
DialogUtil.getInstance().showContent((int)((progress.currentSize/(float)progress.totalSize)*100)+"%");
}
});
}
安装app
private void installApp(File file){
// setPermission(file.getPath());
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra("name", "");
intent.addCategory("android.intent.category.DEFAULT");
String packageName = getPackageName();
Uri data;
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){ //8.0以上需要在清单文件中写上权限:REQUEST_INSTALL_PACKAGES。小米的时候并不需要getPackageManager().canRequestPackageInstalls()的结果为true才做安装操作,会自动弹出允许安装未知来源的App的系统弹窗
// boolean b = getPackageManager().canRequestPackageInstalls();
// 临时允许
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
data = FileProvider.getUriForFile(this, packageName + ".fileprovider", file);
}else if (Build.VERSION.SDK_INT >= 24){
// 临时允许
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
data = FileProvider.getUriForFile(this, packageName + ".fileprovider", file);
}else {
data = Uri.fromFile(file);
}
intent.setDataAndType(data, "application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);//这一句后不能做退出当前app的操作:比如:system.exit(),或者结束所有activity。因为部分手机(华为,三星)在安装最新版本时会报解析包错误,而无法安装。
}
/**
* 提升读写权限
*
* @param filePath 文件路径
*/
private static void setPermission(String filePath) {
String command = "chmod " + "777" + " " + filePath;
Runtime runtime = Runtime.getRuntime();
try {
runtime.exec(command);
} catch (IOException e) {
e.printStackTrace();
}
}
// 获取当前App版本
private String getLocalVersion() {
// 获取包管理者对象
PackageManager pm = getPackageManager();
try {
// 获取包的详细信息
PackageInfo info = pm.getPackageInfo(getPackageName(), 0);
// 获取版本号和版本名称
return info.versionName;
} catch (Exception e) {
return "";
}
}
特别说明:
1、app覆盖安装时assets中的资源文件如果重名则不会更新到最新。所以如果重名但是文件内容不同,就需要改名字了。
2、apk文件最好不要存放在getExternalCacheDir()目录下,可能出现无法调起系统的安装。(此问题在android5.1可复现)
3、缓存到本地存储的apk的文件名最好不要一致,不然可能导致安装时报:解析包时错误。如果必须重名那么要先删除已有的,之后再缓存。(此问题在android5.1可复现)
鸣谢:
https://www.jianshu.com/p/121bbb07cb07