1 概述
InstallInstalling是普通安装流程中,系统启动的第二个Activity
InstallStart封装的数据有
包信息,应用信息 用户id,包括启动进程传来的原始数据信息
2 InstallInstalling都做了什么
作为系统应用中第二个Activity,他同样要遵循生命周期来做一些事情
2.1 onCreate
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//通过INTENT_ATTR_APPLICATION_INFO,获取应用进程(发起安装进程)相关信息
ApplicationInfo appInfo = getIntent()
.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
//获取应用进程URI数据
mPackageURI = getIntent().getData();
if ("package".equals(mPackageURI.getScheme())) {
/**
* case 当前URI的Scheme为package
*
* 过程 跨进程根据应用进程提供的包信息来安装,跳转安装成功activity
*/
try {
getPackageManager().installExistingPackage(appInfo.packageName);
launchSuccess();
} catch (PackageManager.NameNotFoundException e) {
launchFailure(PackageInstaller.STATUS_FAILURE,
PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
} else {
/**
* case 当前URI的Scheme不为package
* 过程
* 1 创建file对象指定安装包路径
* 2 封装应用程序相关信息
* 3 设置提示框相关UI和按钮行为 (用户可以点击取消按钮取消当次安装)
* 4 检查当前Activity是否保存上次安装操作中的session_id install_id
* 存在的话添加监听器
* 5 首先创建会话属性PackageInstaller.SessionParams
* 检查intent是否携带referrerUri
* 根据refererUrl是否携带可以判断当次安装资源是本地还是网络下载
* 当次应用是否为及时应用
* 设置referrerUri
* 设置安装包来源URI
* 设置安装包所属user
* 解析安装包部分数据
* 设置安装包名称
* 设置安装原因
* 解析安装包获取包名,安装位置,安装大小
*
* 上述提到的这些信息都会保存到我们新创建的会话属性中 PackageInstaller.SessionParams params
*
* 6 根据会话属性创建会话拿到会话id
*/
final File sourceFile = new File(mPackageURI.getPath());
PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
mAlert.setIcon(as.icon);
mAlert.setTitle(as.label);
mAlert.setView(R.layout.install_content_view);
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
(ignored, ignored2) -> {
if (mInstallingTask != null) {
mInstallingTask.cancel(true);
}
if (mSessionId > 0) {
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
mSessionId = 0;
}
setResult(RESULT_CANCELED);
finish();
}, null);
setupAlert();
requireViewById(R.id.installing).setVisibility(View.VISIBLE);
if (savedInstanceState != null) {
mSessionId = savedInstanceState.getInt(SESSION_ID);
mInstallId = savedInstanceState.getInt(INSTALL_ID);
// Reregister for result; might instantly call back if result was delivered while
// activity was destroyed
try {
InstallEventReceiver.addObserver(this, mInstallId,
this::launchFinishBasedOnResult);
} catch (EventResultPersister.OutOfIdsException e) {
// Does not happen
}
} else {
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
final Uri referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
params.setPackageSource(
referrerUri != null ? PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
: PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE);
params.setInstallAsInstantApp(false);
params.setReferrerUri(referrerUri);
params.setOriginatingUri(getIntent()
.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI));
params.setOriginatingUid(getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
UID_UNKNOWN));
params.setInstallerPackageName(getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME));
params.setInstallReason(PackageManager.INSTALL_REASON_USER);
File file = new File(mPackageURI.getPath());
try {
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
input.reset(), file, /* flags */ 0);
if (result.isError()) {
Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
Log.e(LOG_TAG,
"Cannot calculate installed size " + file + ". Try only apk size.");
params.setSize(file.length());
} else {
final PackageLite pkg = result.getResult();
params.setAppPackageName(pkg.getPackageName());
params.setInstallLocation(pkg.getInstallLocation());
params.setSize(InstallLocationUtils.calculateInstalledSize(pkg,
params.abiOverride));
}
} catch (IOException e) {
Log.e(LOG_TAG,
"Cannot calculate installed size " + file + ". Try only apk size.");
params.setSize(file.length());
}
try {
mInstallId = InstallEventReceiver
.addObserver(this, EventResultPersister.GENERATE_NEW_ID,
this::launchFinishBasedOnResult);
} catch (EventResultPersister.OutOfIdsException e) {
launchFailure(PackageInstaller.STATUS_FAILURE,
PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
try {
mSessionId = getPackageManager().getPackageInstaller().createSession(params);
} catch (IOException e) {
launchFailure(PackageInstaller.STATUS_FAILURE,
PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
}
mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);
}
}
总结:onCreate 生命周期,主要就是为新的安装包创建安装会话
2.2 onResume
protected void onResume() {
super.onResume();
// This is the first onResume in a single life of the activity
if (mInstallingTask == null) {
/**
* case 安装任务为空
*
* 过程 创建安装异步任务,并且执行异步任务
*/
PackageInstaller installer = getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
if (sessionInfo != null && !sessionInfo.isActive()) {
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
} else {
// we will receive a broadcast when the install is finished
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
}
}
}
总结onResume主要就是创建异步任务,执行安装相关操作
3 附加
既然这里提到了异步任务,我们也刚好可以了解一下AyncTask
3.1 概述
AsyncTask 是 Android 提供的一个用于在后台执行异步任务的类。它提供了一种简单的方法来执行后台操作,并在主线程中更新 UI。AsyncTask 是在 Android 中比较常用的工具之一,特别适合于需要在后台执行任务并更新 UI 的场景。
AsyncTask 类包含四个步骤方法:
onPreExecute():在后台任务执行前被调用,通常用于在执行后台任务前做一些初始化操作,例如显示进度条等。
doInBackground(Params...):在后台执行耗时操作,该方法运行在后台线程中,用于执行耗时任务,但不能操作 UI。
onProgressUpdate(Progress...):在调用 publishProgress(Progress...) 后被调用,用于更新任务的执行进度,可以在这个方法中更新 UI。
onPostExecute(Result):在后台任务执行完毕并通过 return 语句返回结果时被调用,用于处理执行结果并更新 UI。
3.2 异步下载任务
private final class InstallingAsyncTask extends AsyncTask<Void, Void,
PackageInstaller.Session> {
volatile boolean isDone;
@Override
protected PackageInstaller.Session doInBackground(Void... params) {
PackageInstaller.Session session;
try {
//首先获取当次安装会话
session = getPackageManager().getPackageInstaller().openSession(mSessionId);
} catch (IOException e) {
synchronized (this) {
isDone = true;
notifyAll();
}
return null;
}
//设置初始进度
session.setStagingProgress(0);
try {
//根据mPackageURI创建file对象
File file = new File(mPackageURI.getPath());
//获取输入流读取文件数据写入到内存
try (InputStream in = new FileInputStream(file)) {
long sizeBytes = file.length();
//通过session.openWrite方法创建了输出流
try (OutputStream out = session
.openWrite("PackageInstaller", 0, sizeBytes)) {
//设置数据缓存数组
byte[] buffer = new byte[1024 * 1024];
while (true) {
//输入流读取数据
int numRead = in.read(buffer);
if (numRead == -1) {
session.fsync(out);
break;
}
if (isCancelled()) {
session.close();
break;
}
//输出流将数据写到session中
out.write(buffer, 0, numRead);
if (sizeBytes > 0) {
//同步每次写入进度
float fraction = ((float) numRead / (float) sizeBytes);
session.addProgress(fraction);
}
}
}
}
return session;
} catch (IOException | SecurityException e) {
Log.e(LOG_TAG, "Could not write package", e);
//出现异常关闭会话
session.close();
return null;
} finally {
synchronized (this) {
isDone = true;
notifyAll();
}
}
}
@Override
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
//case 1 安装会话不为空 创建intent对象
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
//对intent进行数据封装
broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
//封装包名
broadcastIntent.setPackage(getPackageName());
//封装安装id
broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
//获取待处理的广播intent
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallInstalling.this,
mInstallId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
//这里执行commit操作将IntentSender提交
/**
* IntentSender 用于允许一个应用程序的组件请求另一个应用程序的组件执行操作,
* 即使此时两者可能不在运行中。
*/
session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
//失败的话丢弃会话
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
if (!isCancelled()) {
//执行安装失败通知
launchFailure(PackageInstaller.STATUS_FAILURE,
PackageManager.INSTALL_FAILED_INVALID_APK, null);
}
}
}
}
}
总结:当次异步任务主要就是,读取文件数据写到session中,然后构建了广播意图调用session.commit方法
接下来就是普通安装的session处理流程