Android包管理機制(三)PMS處理APK的安裝
相關文章
Android%E5%8C%85%E7%AE%A1%E7%90%86%E6%9C%BA%E5%88%B6/" target="_blank" rel="nofollow,noindex">Android包管理機制系列前言
在上一篇文章 Android包管理機制(二)PackageInstaller安裝APK 中,我們學習了PackageInstaller是如何安裝APK的,最後會將APK的資訊交由PMS處理。那麼PMS是如何處理的呢?這篇文章會給你答案。
1.PackageHandler處理安裝訊息
APK的資訊交由PMS後,PMS通過向PackageHandler傳送訊息來驅動APK的複製和安裝工作。
先來檢視PackageHandler處理安裝訊息的呼叫時序圖。

接著上一篇文章的程式碼邏輯來檢視PMS的installStage方法。
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
void installStage(String packageName, File stagedDir, String stagedCid, IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, String installerPackageName, int installerUid, UserHandle user, Certificate[][] certificates) { ... final Message msg = mHandler.obtainMessage(INIT_COPY);//1 final int installReason = fixUpInstallReason(installerPackageName, installerUid, sessionParams.installReason); final InstallParams params = new InstallParams(origin, null, observer, sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid, verificationInfo, user, sessionParams.abiOverride, sessionParams.grantedRuntimePermissions, certificates, installReason);//2 params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params)); msg.obj = params; ... mHandler.sendMessage(msg);//3 }
註釋2處建立InstallParams,它對應於包的安裝資料。註釋1處建立了型別為INIT_COPY的訊息,在註釋3處將InstallParams通過訊息傳送出去。
1.1 對INIT_COPY的訊息的處理
處理INIT_COPY型別的訊息的程式碼如下所示。
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#PackageHandler
void doHandleMessage(Message msg) { switch (msg.what) { case INIT_COPY: { HandlerParams params = (HandlerParams) msg.obj; int idx = mPendingInstalls.size(); if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params); //mBound用於標識是否綁定了服務,預設值為false if (!mBound) {//1 Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS", System.identityHashCode(mHandler)); //如果沒有繫結服務,重新繫結,connectToService方法內部如果繫結成功會將mBound置為true if (!connectToService()) {//2 Slog.e(TAG, "Failed to bind to media container service"); params.serviceError(); Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS", System.identityHashCode(mHandler)); if (params.traceMethod != null) { Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod, params.traceCookie); } //繫結服務失敗則return return; } else { //繫結服務成功,將請求新增到ArrayList型別的mPendingInstalls中,等待處理 mPendingInstalls.add(idx, params); } } else { //已經繫結服務 mPendingInstalls.add(idx, params); if (idx == 0) { mHandler.sendEmptyMessage(MCS_BOUND);//3 } } break; } .... } } }
PackageHandler繼承自Handler,它被定義在PMS中,doHandleMessage方法用於處理各個型別的訊息,來檢視對INIT_COPY型別訊息的處理。註釋1處的mBound用於標識是否綁定了DefaultContainerService,預設值為false。DefaultContainerService是用於檢查和複製可移動檔案的服務,這是一個比較耗時的操作,因此DefaultContainerService沒有和PMS執行在同一程序中,它執行在com.android.defcontainer程序,通過IMediaContainerService和PMS進行IPC通訊,如下圖所示。

註釋2處的connectToService方法用來繫結DefaultContainerService,註釋3處傳送MCS_BOUND型別的訊息,觸發處理第一個安裝請求。
檢視註釋2處的connectToService方法:
**frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#PackageHandler **
private boolean connectToService() { if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" + " DefaultContainerService"); Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {//1 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); mBound = true;//2 return true; } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); return false; }
註釋2處如果繫結DefaultContainerService成功,mBound會置為ture 。註釋1處的bindServiceAsUser方法會傳入mDefContainerConn,bindServiceAsUser方法的處理邏輯和我們呼叫bindService是類似的,服務建立連線後,會呼叫onServiceConnected方法:
**frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java **
class DefaultContainerConnection implements ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service) { if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected"); final IMediaContainerService imcs = IMediaContainerService.Stub .asInterface(Binder.allowBlocking(service)); mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, Object));//1 } public void onServiceDisconnected(ComponentName name) { if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected"); } }
註釋1處傳送了MCS_BOUND型別的訊息,與 PackageHandler.doHandleMessage
方法的註釋3處不同的是,這裡傳送訊息帶了Object型別的引數,這裡會對這兩種情況來進行講解,一種是訊息不帶Object型別的引數,一種是訊息帶Object型別的引數。
1.2 對MCS_BOUND型別的訊息的處理
訊息不帶Object型別的引數
檢視對MCS_BOUND型別訊息的處理:
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
case MCS_BOUND: { if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound"); if (msg.obj != null) {//1 mContainerService = (IMediaContainerService) msg.obj;//2 Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS", System.identityHashCode(mHandler)); } if (mContainerService == null) {//3 if (!mBound) {//4 Slog.e(TAG, "Cannot bind to media container service"); for (HandlerParams params : mPendingInstalls) { params.serviceError();//5 Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", System.identityHashCode(params)); if (params.traceMethod != null) { Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod, params.traceCookie); } return; } //繫結失敗,清空安裝請求佇列 mPendingInstalls.clear(); } else { //繼續等待繫結服務 Slog.w(TAG, "Waiting to connect to media container service"); } } else if (mPendingInstalls.size() > 0) { ... else { Slog.w(TAG, "Empty queue"); } break; }
如果訊息不帶Object型別的引數,就無法滿足註釋1處的條件,註釋2處的IMediaContainerService型別的mContainerService也無法被賦值,這樣就滿足了註釋3處的條件。
如果滿足註釋4處的條件,說明還沒有繫結服務,而此前已經在 PackageHandler.doHandleMessage
方法的註釋2處呼叫繫結服務的方法了,這顯然是不正常的,因此在註釋5處負責處理服務發生錯誤的情況。如果不滿足註釋4處的條件,說明已經繫結服務了,就會打印出系統log,告知使用者等待系統繫結服務。
訊息帶Object型別的引數
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
case MCS_BOUND: { if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound"); if (msg.obj != null) { ... } if (mContainerService == null) {//1 ... } else if (mPendingInstalls.size() > 0) {//2 HandlerParams params = mPendingInstalls.get(0);//3 if (params != null) { Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", System.identityHashCode(params)); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy"); if (params.startCopy()) {//4 if (DEBUG_SD_INSTALL) Log.i(TAG, "Checking for more work or unbind..."); //如果APK安裝成功,刪除本次安裝請求 if (mPendingInstalls.size() > 0) { mPendingInstalls.remove(0); } if (mPendingInstalls.size() == 0) { if (mBound) { //如果沒有安裝請求了,傳送解綁服務的請求 if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting delayed MCS_UNBIND"); removeMessages(MCS_UNBIND); Message ubmsg = obtainMessage(MCS_UNBIND); sendMessageDelayed(ubmsg, 10000); } } else { if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting MCS_BOUND for next work"); //如果還有其他的安裝請求,接著傳送MCS_BOUND訊息繼續處理剩餘的安裝請求 mHandler.sendEmptyMessage(MCS_BOUND);//5 } } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); }else { Slog.w(TAG, "Empty queue");//6 } break; }
如果MCS_BOUND型別訊息帶Object型別的引數就不會滿足註釋1處的條件,就會呼叫註釋2處的判斷,如果安裝請求數不大於0就會打印出註釋6處的log,說明安裝請求佇列是空的。安裝完一個APK後,就會在註釋5處發出MSC_BOUND訊息,繼續處理剩下的安裝請求直到安裝請求佇列為空。
註釋3處得到安裝請求佇列第一個請求HandlerParams ,如果HandlerParams 不為null就會呼叫註釋4處的HandlerParams的startCopy方法,用於開始複製APK的流程。
2.複製APK
先來檢視複製APK的時序圖。

HandlerParams是PMS中的抽象類,它的實現類為PMS的內部類InstallParams。HandlerParams的startCopy方法如下所示。
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#HandlerParams
final boolean startCopy() { boolean res; try { if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this); //startCopy方法嘗試的次數,超過了4次,就放棄這個安裝請求 if (++mRetries > MAX_RETRIES) {//1 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up"); mHandler.sendEmptyMessage(MCS_GIVE_UP);//2 handleServiceError(); return false; } else { handleStartCopy();//3 res = true; } } catch (RemoteException e) { if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT"); mHandler.sendEmptyMessage(MCS_RECONNECT); res = false; } handleReturnCode();//4 return res; }
註釋1處的mRetries用於記錄startCopy方法呼叫的次數,呼叫startCopy方法時會先自動加1,如果次數大於4次就放棄這個安裝請求:在註釋2處傳送MCS_GIVE_UP型別訊息,將第一個安裝請求(本次安裝請求)從安裝請求佇列mPendingInstalls中移除掉。註釋4處用於處理複製APK後的安裝APK邏輯,第3小節中會再次提到它。註釋3處呼叫了抽象方法handleStartCopy,它的實現在InstallParams中,如下所示。
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#InstallParams
public void handleStartCopy() throws RemoteException { ... //確定APK的安裝位置。onSd:安裝到SD卡, onInt:內部儲存即Data分割槽,ephemeral:安裝到臨時儲存(Instant Apps安裝) final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0; final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0; final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0; PackageInfoLite pkgLite = null; if (onInt && onSd) { // APK不能同時安裝在SD卡和Data分割槽 Slog.w(TAG, "Conflicting flags specified for installing on both internal and external"); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; //安裝標誌衝突,Instant Apps不能安裝到SD卡中 } else if (onSd && ephemeral) { Slog.w(TAG,"Conflicting flags specified for installing ephemeral on external"); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { //獲取APK的少量的資訊 pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride);//1 if (DEBUG_EPHEMERAL && ephemeral) { Slog.v(TAG, "pkgLite for install: " + pkgLite); } ... if (ret == PackageManager.INSTALL_SUCCEEDED) { //判斷安裝的位置 int loc = pkgLite.recommendedInstallLocation; if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) { ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) { ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; } ... }else{ loc = installLocationPolicy(pkgLite);//2 ... } } //根據InstallParams建立InstallArgs物件 final InstallArgs args = createInstallArgs(this);//3 mArgs = args; if (ret == PackageManager.INSTALL_SUCCEEDED) { ... if (!origin.existing && requiredUid != -1 && isVerificationEnabled( verifierUser.getIdentifier(), installFlags, installerUid)) { ... } else{ ret = args.copyApk(mContainerService, true);//4 } } mRet = ret; }
handleStartCopy方法的程式碼很多,這裡擷取關鍵的部分。
註釋1處通過IMediaContainerService跨程序呼叫DefaultContainerService的getMinimalPackageInfo方法,該方法輕量解析APK並得到APK的少量資訊,輕量解析的原因是這裡不需要得到APK的全部資訊,APK的少量資訊會封裝到PackageInfoLite中。接著在註釋2處確定APK的安裝位置。註釋3處建立了InstallArgs,InstallArgs 是一個抽象類,定義了APK的安裝邏輯,比如複製和重新命名APK等,它有3個子類,都被定義在PMS中,如下圖所示。

其中FileInstallArgs用於處理安裝到非ASEC的儲存空間的APK,也就是內部儲存空間(Data分割槽),AsecInstallArgs用於處理安裝到ASEC中(mnt/asec)即SD卡中的APK。MoveInstallArgs用於處理已安裝APK的移動的邏輯。
對APK進行檢查後就會在註釋4處呼叫InstallArgs的copyApk方法進行安裝。
不同的InstallArgs子類會有著不同的處理,這裡以FileInstallArgs為例。FileInstallArgs的copyApk方法中會直接return FileInstallArgs的doCopyApk方法:
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#FileInstallArgsprivate int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { ... try { final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0; //建立臨時檔案儲存目錄 final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);//1 codeFile = tempDir; resourceFile = tempDir; } catch (IOException e) { Slog.w(TAG, "Failed to create copy file: " + e); return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } ... int ret = PackageManager.INSTALL_SUCCEEDED; ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);//2 ... return ret; }
註釋1處用於建立臨時儲存目錄,比如/data/app/vmdl18300388.tmp,其中18300388是安裝的sessionId。註釋2處通過IMediaContainerService跨程序呼叫DefaultContainerService的copyPackage方法,這個方法會在DefaultContainerService所在的程序中將APK複製到臨時儲存目錄,比如/data/app/vmdl18300388.tmp/base.apk。目前為止APK的複製工作就完成了,接著就是APK的安裝過程了。
3.安裝APK
照例先來檢視安裝APK的時序圖。

安裝APK_副本.png
我們回到APK的複製呼叫鏈的頭部方法:HandlerParams的startCopy方法,在註釋4處會呼叫handleReturnCode方法,它的實現在InstallParams中,如下所示。
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.javavoid handleReturnCode() { if (mArgs != null) { processPendingInstall(mArgs, mRet); } } private void processPendingInstall(final InstallArgs args, final int currentStatus) { mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); PackageInstalledInfo res = new PackageInstalledInfo(); res.setReturnCode(currentStatus); res.uid = -1; res.pkg = null; res.removedInfo = null; if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { //安裝前處理 args.doPreInstall(res.returnCode);//1 synchronized (mInstallLock) { installPackageTracedLI(args, res);//2 } //安裝後收尾 args.doPostInstall(res.returnCode, res.uid);//3 } ... } }); }
handleReturnCode方法中只調用了processPendingInstall方法,註釋1處用於檢查APK的狀態的,在安裝前確保安裝環境的可靠,如果不可靠會清除複製的APK檔案,註釋3處用於處理安裝後的收尾操作,如果安裝不成功,刪除掉安裝相關的目錄與檔案。主要來看註釋2處的installPackageTracedLI方法,其內部會呼叫PMS的installPackageLI方法。
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) { ... PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setDisplayMetrics(mMetrics); pp.setCallback(mPackageParserCallback); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); final PackageParser.Package pkg; try { //解析APK pkg = pp.parsePackage(tmpPackageFile, parseFlags);//1 } catch (PackageParserException e) { res.setError("Failed parse during installPackageLI", e); return; } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } ... pp = null; String oldCodePath = null; boolean systemApp = false; synchronized (mPackages) { // 檢查APK是否存在 if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { String oldName = mSettings.getRenamedPackageLPr(pkgName);//獲取沒被改名前的包名 if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName) && mPackages.containsKey(oldName)) { pkg.setPackageName(oldName);//2 pkgName = pkg.packageName; replace = true;//設定標誌位表示是替換安裝 if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName=" + oldName + " pkgName=" + pkgName); } ... } PackageSetting ps = mSettings.mPackages.get(pkgName); //檢視Settings中是否存有要安裝的APK的資訊,如果有就獲取簽名信息 if (ps != null) {//3 if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps); PackageSetting signatureCheckPs = ps; if (pkg.applicationInfo.isStaticSharedLibrary()) { SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg); if (libraryEntry != null) { signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk); } } //檢查簽名的正確性 if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) { if (!checkUpgradeKeySetLP(signatureCheckPs, pkg)) { res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + pkg.packageName + " upgrade keys do not match the " + "previously installed version"); return; } } ... } int N = pkg.permissions.size(); for (int i = N-1; i >= 0; i--) { //遍歷每個許可權,對許可權進行處理 PackageParser.Permission perm = pkg.permissions.get(i); BasePermission bp = mSettings.mPermissions.get(perm.info.name); } } } if (systemApp) { if (onExternal) { //系統APP不能在SD卡上替換安裝 res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION, "Cannot install updates to system apps on sdcard"); return; } else if (instantApp) { //系統APP不能被Instant App替換 res.setError(INSTALL_FAILED_INSTANT_APP_INVALID, "Cannot update a system app with an instant app"); return; } } ... //重新命名臨時檔案 if (!args.doRename(res.returnCode, pkg, oldCodePath)) {//4 res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename"); return; } startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg); try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags, "installPackageLI")) { if (replace) {//5 //替換安裝 ... replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user, installerPackageName, res, args.installReason); } else { //安裝新的APK installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES, args.user, installerPackageName, volumeUuid, res, args.installReason); } } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null) { //更新應用程式所屬的使用者 res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); ps.setUpdateAvailable(false /*updateAvailable*/); } ... } }
installPackageLI方法的程式碼有將近500行,這裡擷取主要的部分,主要做了幾件事:
- 建立PackageParser解析APK。
- 檢查APK是否存在,如果存在就獲取此前沒被改名前的包名並在註釋1處賦值給PackageParser.Package型別的pkg,在註釋3處將標誌位replace置為true表示是替換安裝。
- 註釋3處,如果Settings中儲存有要安裝的APK的資訊,說明此前安裝過該APK,則需要校驗APK的簽名信息,確保安全的進行替換。
- 在註釋4處將臨時檔案重新命名,比如前面提到的/data/app/vmdl18300388.tmp/base.apk,重新命名為/data/app/包名-1/base.apk。這個新命名的包名會帶上一個數字字尾1,每次升級一個已有的App,這個數字會不斷的累加。
- 系統APP的更新安裝會有兩個限制,一個是系統APP不能在SD卡上替換安裝,另一個是系統APP不能被Instant App替換。
- 註釋5處根據replace來做區分,如果是替換安裝就會呼叫replacePackageLIF方法,其方法內部還會對系統APP和非系統APP進行區分處理,如果是新安裝APK會呼叫installNewPackageLIF方法。
這裡我們以新安裝APK為例,會呼叫PMS的installNewPackageLIF方法。
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags, int scanFlags, UserHandle user, String installerPackageName, String volumeUuid, PackageInstalledInfo res, int installReason) { ... try { //掃描APK PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags, System.currentTimeMillis(), user); //更新Settings資訊 updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason); if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { //安裝成功後,為新安裝的應用程式準備資料 prepareAppDataAfterInstallLIF(newPackage); } else { //安裝失敗則刪除APK deletePackageLIF(pkgName, UserHandle.ALL, false, null, PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null); } } catch (PackageManagerException e) { res.setError("Package couldn't be installed in " + pkg.codePath, e); } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); }
installNewPackageLIF主要做了以下3件事:
- 掃描APK,將APK的資訊儲存在PackageParser.Package型別的newPackage中,一個Package的資訊包含了1個base APK以及0個或者多個split APK。
- 更新該APK對應的Settings資訊,Settings用於儲存所有包的動態設定。
- 如果安裝成功就為新安裝的應用程式準備資料,安裝失敗就刪除APK。
安裝APK的過程就講到這裡,就不再往下分析下去,有興趣的同學可以接著深挖。
4. 總結
本文主要講解了PMS是如何處理APK安裝的,主要有幾個步驟:
- PackageInstaller安裝APK時會將APK的資訊交由PMS處理,PMS通過向PackageHandler傳送訊息來驅動APK的複製和安裝工作。
- PMS傳送INIT_COPY和MCS_BOUND型別的訊息,控制PackageHandler來繫結DefaultContainerService,完成複製APK等工作。
- 複製APK完成後,會開始進行安裝APK的流程,包括安裝前的檢查、安裝APK和安裝後的收尾工作。