1 概述
InstallInstalling是普通安裝流程中括堤,系統(tǒng)啟動的第二個Activity
InstallStart封裝的數(shù)據(jù)有
包信息,應用信息 用戶id抱既,包括啟動進程傳來的原始數(shù)據(jù)信息
2 InstallInstalling都做了什么
作為系統(tǒng)應用中第二個Activity剪返,他同樣要遵循生命周期來做一些事情
2.1 onCreate
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//通過INTENT_ATTR_APPLICATION_INFO,獲取應用進程(發(fā)起安裝進程)相關信息
ApplicationInfo appInfo = getIntent()
.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
//獲取應用進程URI數(shù)據(jù)
mPackageURI = getIntent().getData();
if ("package".equals(mPackageURI.getScheme())) {
/**
* case 當前URI的Scheme為package
*
* 過程 跨進程根據(jù)應用進程提供的包信息來安裝倚搬,跳轉安裝成功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 創(chuàng)建file對象指定安裝包路徑
* 2 封裝應用程序相關信息
* 3 設置提示框相關UI和按鈕行為 (用戶可以點擊取消按鈕取消當次安裝)
* 4 檢查當前Activity是否保存上次安裝操作中的session_id install_id
* 存在的話添加監(jiān)聽器
* 5 首先創(chuàng)建會話屬性PackageInstaller.SessionParams
* 檢查intent是否攜帶referrerUri
* 根據(jù)refererUrl是否攜帶可以判斷當次安裝資源是本地還是網(wǎng)絡下載
* 當次應用是否為及時應用
* 設置referrerUri
* 設置安裝包來源URI
* 設置安裝包所屬user
* 解析安裝包部分數(shù)據(jù)
* 設置安裝包名稱
* 設置安裝原因
* 解析安裝包獲取包名冶共,安裝位置,安裝大小
*
* 上述提到的這些信息都會保存到我們新創(chuàng)建的會話屬性中 PackageInstaller.SessionParams params
*
* 6 根據(jù)會話屬性創(chuàng)建會話拿到會話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 生命周期每界,主要就是為新的安裝包創(chuàng)建安裝會話
2.2 onResume
protected void onResume() {
super.onResume();
// This is the first onResume in a single life of the activity
if (mInstallingTask == null) {
/**
* case 安裝任務為空
*
* 過程 創(chuàng)建安裝異步任務捅僵,并且執(zhí)行異步任務
*/
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主要就是創(chuàng)建異步任務,執(zhí)行安裝相關操作
3 附加
既然這里提到了異步任務眨层,我們也剛好可以了解一下AyncTask
3.1 概述
AsyncTask 是 Android 提供的一個用于在后臺執(zhí)行異步任務的類庙楚。它提供了一種簡單的方法來執(zhí)行后臺操作,并在主線程中更新 UI趴樱。AsyncTask 是在 Android 中比較常用的工具之一馒闷,特別適合于需要在后臺執(zhí)行任務并更新 UI 的場景。
AsyncTask 類包含四個步驟方法:
onPreExecute():在后臺任務執(zhí)行前被調用叁征,通常用于在執(zhí)行后臺任務前做一些初始化操作纳账,例如顯示進度條等。
doInBackground(Params...):在后臺執(zhí)行耗時操作航揉,該方法運行在后臺線程中塞祈,用于執(zhí)行耗時任務,但不能操作 UI帅涂。
onProgressUpdate(Progress...):在調用 publishProgress(Progress...) 后被調用议薪,用于更新任務的執(zhí)行進度,可以在這個方法中更新 UI媳友。
onPostExecute(Result):在后臺任務執(zhí)行完畢并通過 return 語句返回結果時被調用斯议,用于處理執(zhí)行結果并更新 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 {
//根據(jù)mPackageURI創(chuàng)建file對象
File file = new File(mPackageURI.getPath());
//獲取輸入流讀取文件數(shù)據(jù)寫入到內存
try (InputStream in = new FileInputStream(file)) {
long sizeBytes = file.length();
//通過session.openWrite方法創(chuàng)建了輸出流
try (OutputStream out = session
.openWrite("PackageInstaller", 0, sizeBytes)) {
//設置數(shù)據(jù)緩存數(shù)組
byte[] buffer = new byte[1024 * 1024];
while (true) {
//輸入流讀取數(shù)據(jù)
int numRead = in.read(buffer);
if (numRead == -1) {
session.fsync(out);
break;
}
if (isCancelled()) {
session.close();
break;
}
//輸出流將數(shù)據(jù)寫到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);
//出現(xiàn)異常關閉會話
session.close();
return null;
} finally {
synchronized (this) {
isDone = true;
notifyAll();
}
}
}
@Override
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
//case 1 安裝會話不為空 創(chuàng)建intent對象
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
//對intent進行數(shù)據(jù)封裝
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);
//這里執(zhí)行commit操作將IntentSender提交
/**
* IntentSender 用于允許一個應用程序的組件請求另一個應用程序的組件執(zhí)行操作醇锚,
* 即使此時兩者可能不在運行中哼御。
*/
session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
//失敗的話丟棄會話
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
if (!isCancelled()) {
//執(zhí)行安裝失敗通知
launchFailure(PackageInstaller.STATUS_FAILURE,
PackageManager.INSTALL_FAILED_INVALID_APK, null);
}
}
}
}
}
總結:當次異步任務主要就是坯临,讀取文件數(shù)據(jù)寫到session中,然后構建了廣播意圖調用session.commit方法
接下來就是普通安裝的session處理流程