近期做了一个车机上的应用商店App,安装包下载完成后需要通过静默安装的方式将应用在后台进行无声无息的安装,这里使用的安装方法是通过 PackageInstaller.Secssion 实现静默安装,该接口需要获取系统权限 android.permission.INSTALL_PACKAGES。
1.其主要通过下面三个步骤进行的:
//1.创建Session
int sessionId = packageInstaller.createSession(params);
//2.开启Session
session = packageInstaller.openSession(sessionId);
//3.获取输出流,用于将apk写入session
outputStream = session.openWrite(apkName, 0, apkFile.length());
//4.提交安装完成Intent
session.commit(intentSender);
2.主要代码:
(1)installApk方法:
public void installApk(String apkFilePath, InstallObserver installObserver) {
/*apkFilePath:这里我们首先传入的是安装包的路径 installObserver:自定义安装的回调,不需要可以删了*/
File apkFile = new File(apkFilePath);
//判断路径下的文件是否存在
if (!apkFile.exists()) {
LogUtils.error(TAG, "apkFile is null...");
return;
}
String packageName = "";
//获取安装包的信息
PackageInfo packageInfo = AppStoreApplication.getApp().getPkgManager().getPackageArchiveInfo(apkFilePath,
PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
if (packageInfo != null) {
packageName = packageInfo.packageName;
String versionName = packageInfo.versionName
}
//获取packageInstaller,后面用来创建PackageInstaller.Session
PackageInstaller packageInstaller = AppStoreApplication.getApp().getPkgManager().getPackageInstaller();
//获取创建PackageInstaller.Session的参数
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
/*指示将在此会话中交付的所有APK的总大小(以字节为单位),系统可以使用它来确保在继续之前存在足够的磁盘空间,
或者估计安装在外部存储上的容器大小*/
sessionParams.setSize(apkFile.length());
PackageInstaller.Session session = null;
try {
//代表一个session的唯一ID,这里我是在全局变量中声明,因为后面的另外一个方法用到了这个sessionId
mSessionId = packageInstaller.createSession(sessionParams);
if (mSessionId != -1) {
//也就是在这个外部的onTransfesApkFile()方法中,将会用到sessionId
boolean copySuccess = onTransfesApkFile(apkFilePath, packageName);
if (copySuccess) {
session = AppStoreApplication.getApp().getPkgManager().getPackageInstaller().openSession(mSessionId);
//设置安装完成后需要发送的一个自定义安装结果广播,这里我设置了App的NAME,VERSION,PACKAGE
Intent intent = new Intent(AppStoreApplication.getApp(),
InstallResultReceiver.class);
intent.setAction(PackageInstaller.EXTRA_STATUS);
intent.putExtra("APP_NAME", localAppInfo.getName());
intent.putExtra("APP_VERSION", localAppInfo.getLastVersion());
intent.putExtra("APP_PACKAGE", localAppInfo.getPackageName());
//执行结束后,发送intent
PendingIntent pendingIntent = PendingIntent.getBroadcast(AppStoreApplication.getApp(),1,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
//这里最终进行session的提交
session.commit(pendingIntent.getIntentSender());
} else {
//此处是安装失败的回调,不需要可以删除
if (installObserver != null) {
installObserver.observer(false, apkFilePath, packageName);
}
}
}
} catch (Exception exception) {
LogUtils.error(TAG, "installApk exception = " + exception.getLocalizedMessage());
} finally {
if (null != session) {
session.close();
}
//安装完成需要删除文件
if (apkFile != null && apkFile.exists()) {
apkFile.delete();
}
}
}
(2)onTransfesApkFile方法(写入数据):
private static boolean onTransfesApkFile(String apkFilePath, String packageName) {
InputStream in = null;
OutputStream out = null;
PackageInstaller.Session session = null;
boolean success = false;
try {
File apkFile = new File(apkFilePath);
//根据sessionId来获取其代表的session
session = AppStoreApplication.getApp().getPkgManager().getPackageInstaller().openSession(mSessionId);
//向session中写入文件数据
out = session.openWrite(packageName + "_base.apk", 0, apkFile.length());
in = new FileInputStream(apkFile);
int total = 0;
int len;
byte[] buffer = new byte[ONE_ZERO_TWO_FOUR * ONE_ZERO_TWO_FOUR];
while ((len = in.read(buffer)) != -1) {
total += len;
out.write(buffer, 0, len);
}
session.fsync(out);
success = true;
} catch (IOException exception) {
exception.printStackTrace();
} finally {
if (null != session) {
session.close();
}
try {
if (null != out) {
out.close();
}
if (null != in) {
in.close();
}
} catch (IOException exception) {
exception.printStackTrace();
}
}
return success;
}
(3)广播接收应用安装的结果:
public class InstallResultReceiver extends BroadcastReceiver {
private static final String TAG = "InstallResultReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (PackageInstaller.EXTRA_STATUS.equals(action)) {
// 接收package install 状态广播
int status = intent.getIntExtra(
PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
String appName = intent.getStringExtra("APP_NAME");
String appPackage = intent.getStringExtra("APP_PACKAGE");
}
}
}
}
记住最后要在AndroidManifest.xml文件里去注册这个广播
:
<receiver
android:name=".receiver.InstallResultReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="1000">
<action android:name="android.content.pm.extra.STATUS" />
</intent-filter>
</receiver>