目前网上大多数关于应用程序中安装apk方法的文章,都会提到智能安装(需要跳转安装页面,但不需用户操作,利用(Google辅助功能),第二种是利用adb命令把安装包push到手机中实现静默安装。
但是本篇实现的方法有所不一样。一种是通过Intent实现(不需要root,跳转到安装界面,需要用户操作),一种是通过PackageInstaller.Session的方法实现静默安装(静默安装都是需要root或者系统签名,不需用户操作)。
一.通过Intent实现安装:
实现步骤:1.在res下创建一个xml文件夹,在xml创建一个资源文件file_paths.xml
根据安装包所在的位置,选择不同的”path“:
a.<files-path name="" path=“”> :表示Context.getFilesDir()目录或者其子目录
目录路径:/data/data/“包名”/files/“name”
b.<cache-path name="" path=“”> :表示Context.getCacheDir()目录或者其子目录
目录路径:/data/data/“包名”/cache/“name”
c.<external-path name="" path=“”> :表示Environment.getExternalStorageDirectory()目录或者其子目录
目录路径:/storage/emulated/0/“name”
d.<external-files-path name="" path=“”> :表示Context.getExternalFilesDir(null)目录或者其子目录。
目录路径:/storage/emulated/0/Android/data/"包名"/files/“name”
e.<external-cache-path name="" path=“”> :表示Context.getExternalCacheDir()目录或者其子目录。
目录路径:/storage/emulated/0/Android/data/"包名"/cache/“name”
当中的path->为external-path的子目录路径,name-> 子目录路径的名称(Uri中代替path显示)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="intentInstall"
path="" />
</paths>
</resources>
2.在工程Main目录下的AndroidManifest文件下,添加一个FileProvider(用于返回安装包的Uri)
android:thorities="包名+provider"
android:name="android.support.v4.content.FileProvider"
android:exported="false"(处于应用安全考虑,假如工程为单模块,建议设为false,多模块可自行斟酌)
android:grantUriPermission="true"(用于权限确认)
添加数据源,resource指向刚刚常见的xml文件。
<provider
android:authorities="com.example.lyybbc.provider"
android:name="android.support.v4.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="file_paths"
android:resource="@xml/file_paths"/>
</provider>
3.代码中完成操作:
a.前提:把1,2步骤完成
获取安装包的包名/路径,安装包的Uri要用FileProvider(content://)开头的
String apkName = "appInstall.apk";
String apkFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + apkName;
File apkFile = new File(apkFilePath);
Uri uri = FileProvider.getUriForFile(getBaseContext(), "om.example.lyybbc.provider", apkFile);
b.通过setDataAndType,把uri传入intent中,添加FLAG_GRANT_PERSISTABLE_URI_PERMISSION。
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
startActivity(intent);
这样,点击安装按钮或者通过Rxjava响应该方法时,会跳转到安装页面,有用户进行安装确认操作。
二.通过PackageInstaller.Session进行静默安装(程序需要系统签名)
实现步骤:1.获取安装包的包名/路径信息,创建安装包的File对象,并获取file的字节大小
String apkName = "appInstall.apk";
String apkFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + apkName;
File apkFile = new File(apkFilePath);
long apkFileLength = apkFile.length();
2.获取PackageManager用于创建Installer对象,并注册安装状态CallBack(监听系统静默安装状态)
PackageManager pm = getPackageManager();
PackageInstaller packageInstaller = pm.getPackageInstaller();
packageInstaller.registerSessionCallback(new PackageInstaller.SessionCallback() {
@Override
public void onCreated(int sessionId) {
Log.e(TAG, "Install Start sessionId-> " + sessionId);
}
@Override
public void onBadgingChanged(int sessionId) {}
@Override
public void onActiveChanged(int sessionId, boolean active) {}
@Override
public void onProgressChanged(int sessionId, float progress) {}
@Override
public void onFinished(int sessionId, boolean success) {
if (success) {
Log.e(TAG, "Silent Install Success");
} else {
Log.e(TAG, "Silent Install Fail");
}
}
});
3.关于安装调用前的准备,创建sessionId,buffer,输入流inputSteam,输出流outputSteam等参数
int count;
int sessionId;
byte[] buffer = new byte[65536];
InputStream inputStream;
OutputStream outputStream;
PackageInstaller.Session session = null;
PackageInstaller.SessionParams sessionParams;
4.实现静默安装操作:
a.创建sessionParams对象(模式选择MODE_FULL_INSTALL)
b.利用sessionParams通过Installer生成sessionId(本次安装任务的ID)
c.installer根据sessionId创建session对象。
sessionParams = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
sessionId = packageInstaller.createSession(sessionParams);
session = packageInstaller.openSession(sessionId);
e.传入文件作为文件流,通过outpustream以字节的方式读取安装包数据,
inputStream = new FileInputStream(apkFile);
outputStream = session.openWrite(apkName, 0, apkFileLength);
while((count = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, count);
float progress = ((float)count / (float)apkFileLength);
session.setStagingProgress(progress); //传入进度数据
}
session.fsync(outputStream); //把outStream的数据都传入session中
inputStream.close();
outputStream.flush();
outputStream.close();
f.生成一个PendingIntent实现广播接受并提交数据
Intent intent = new Intent();
PendingIntent pendingIntent = PendingIntent.getBroadcast(getBaseContext(), 0, intent, 0);
session.commit(pendingIntent.getIntentSender());
g.结束安装后,弃用session
finally {
if (session != null) {
session.abandon(); //安装结束,弃用session
}
}
到此,静默安装完成。callback的onFinished会调用。可以根据返回的布尔型参数判断是否安装成功。假如要监听安装进度,可以通过session.setStagingProgress(progress)方式,向callback传入进度数据(在onProgressChanged获取)
以上两个方法经实践都成功。文章中有错误的地方,望朋友们可以指出,毕竟有批评才有进步。
假如觉得本文有用,那就留个言点个赞吧 ^ ^
完整程序Github地址:https://github.com/sparrowleung/packageInstall