研究應用的安裝過程,老樣子,咱們仍是先從使用入手。java
在Android
中,經過發送Intent
就能夠啓動應用的安裝過程,好比:android
Uri uri = Uri.fromFile(new File(apkFilePath));
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri,"application/vnd.android.package-archive");
startActivity(intent);
複製代碼
而在Android
的系統應用PackageInstaller
中有一個InstallStart
會響應這個Intent
,數組
<activity android:name=".InstallStart" android:theme="@style/Installer" android:exported="true" android:excludeFromRecents="true">
<intent-filter android:priority="1">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.INSTALL_PACKAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
......
</activity>
複製代碼
在InstallStart
中會進行各類Uri
的判斷,最終會跳轉到一個叫作PackageInstallerActivity
的界面。markdown
國內的廠商基本上會在
InstallStart
這裏進行修改,替換爲本身的安裝界面session
對於PackageInstallerActivity
來講,它的主要做用是數據結構
Uri
協議進行解析,包括file
和package
兩種
file
協議會解析APK
文件獲得包信息PackageInfo
Intent
判斷得出該APK
不是未知來源,就會初始化安裝確認界面Dialog
或者跳轉到設置界面當點擊確認安裝後,其實會執行到startInstall()
方法,相關內容以下:併發
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
......
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallInstalling.class);
......
startActivity(newIntent);
finish();
}
複製代碼
上面的邏輯是跳轉到InstallInstalling
界面進行安裝,咱們看看這個Activity
:app
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
......
if ("package".equals(mPackageURI.getScheme())) {
// 直接安裝 package
try {
getPackageManager().installExistingPackage(appInfo.packageName);
launchSuccess();
} catch (PackageManager.NameNotFoundException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
} else {
// 對於 file 安裝,按照session的邏輯去走
// 這部分在onResume中開始
final File sourceFile = new File(mPackageURI.getPath());
......
if (savedInstanceState != null) {
mSessionId = savedInstanceState.getInt(SESSION_ID);
mInstallId = savedInstanceState.getInt(INSTALL_ID);
......
} else {
......
mSessionId = getPackageManager().getPackageInstaller().createSession(params);
......
// 建立應用安裝狀態的Observer
// 真正註冊是在後面 Session 的 commitLocked 中
// InstallEventReceiver 這裏是作了二次封裝,方便進行持久化操做
InstallEventReceiver.addObserver(this, mInstallId,
this::launchFinishBasedOnResult);
}
......
}
}
@Override
protected void onResume() {
super.onResume();
// This is the first onResume in a single life of the activity
if (mInstallingTask == null) {
PackageInstaller installer = getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
// 啓動異步安裝任務 InstallingAsyncTask 進行一系列的session操做
if (sessionInfo != null && !sessionInfo.isActive()) {
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
}
......
}
}
複製代碼
從上面的方法咱們能夠看到安裝操做主要分爲兩個接口:異步
getPackageManager().installExistingPackage()
:主要是對已經並掃描到Settings
的mPackages
集合中的應用進行操做
userID
從集合中查詢應用狀態,若是存在而且應用沒有安裝,就進行一些簡單的配置工做prepareAppDataAfterInstallLIF
方法,建立特定userID
下的應用數據InstallingAsyncTask
任務中的Session
系列操做:一個應用完整的安裝過程從這裏開始 private final class InstallingAsyncTask extends AsyncTask<Void, Void, PackageInstaller.Session> {
volatile boolean isDone;
@Override
protected PackageInstaller.Session doInBackground(Void... params) {
PackageInstaller.Session session;
......
session = getPackageManager().getPackageInstaller().openSession(mSessionId);
try {
File file = new File(mPackageURI.getPath());
try (InputStream in = new FileInputStream(file)) {
long sizeBytes = file.length();
try (OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes)) {
byte[] buffer = new byte[1024 * 1024];
while (true) {
......
out.write(buffer, 0, numRead);
......
}
}
}
return session;
}
......
}
@Override
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
......
session.commit(pendingIntent.getIntentSender());
......
} else {
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
......
}
}
複製代碼
APK
的Uri
,將APK
的信息經過IO
流的形式寫入到PackageInstaller.Session
中PackageInstallerSession.commit()
函數session
這個東西看來很重要,那麼咱們先來看看PackageInstallerSession
吧async
PackageInstallerSession
Android
經過PackageInstallerSession
來表示一次安裝過程,一個PackageInstallerSession
包含一個系統中惟一的一個SessionId
,若是一個應用的安裝必須分幾個階段來完成,即便設備重啓了,也能夠經過這個ID
來繼續安裝過程
PackageInstallerSession
中保存了應用安裝的相關數據,例如,安裝包的路徑、安裝的進度、中間數據保存的目錄等。
PackageInstallerService
提供了接口createSession
來建立一個Session
對象:
@Override
public int createSession(SessionParams params, String installerPackageName, int userId) {
try {
return createSessionInternal(params, installerPackageName, userId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
複製代碼
createSession()
方法將返回一個系統惟一值做爲Session ID
。若是但願再次使用這個Session
,能夠經過接口openSession()
打開它。openSession()
方法的代碼以下所示:
@Override
public IPackageInstallerSession openSession(int sessionId) {
try {
return openSessionInternal(sessionId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
複製代碼
openSession()
方法將返回一個IPackageInstallerSession
對象,它是Binder
服務PackageInstallerSession
的IBinder
對象。在PackageInstallerService
中mSessions
數組保存了全部PackageInstallerSession
對象,定義以下:
@GuardedBy("mSessions")
private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
複製代碼
咱們知道,當PackageManagerService
初始化時會建立PackageInstallerService
服務,在這個服務的初始化函數中會讀取/data/system
目錄下的install_sessions.xml
文件,這個文件中保存了系統中未完成的Install Session
。而後PackageInstallerService
會根據文件的內容建立PackageInstallerSession
對象並插入到mSessions
中。
而對於上面提到的commit()
函數,是真正觸發PMS
安裝的函數,定義以下:
@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
......
mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
......
}
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
......
case MSG_COMMIT:
synchronized (mLock) {
......
commitLocked();
......
}
break;
......
}
return true;
}
};
private void commitLocked() throws PackageManagerException {
......
mRemoteObserver.onUserActionRequired(intent);
......
mPm.installStage(mPackageName, stageDir, localObserver, params,
mInstallerPackageName, mInstallerUid, user, mSigningDetails);
}
複製代碼
commit()
函數最後會調用到PMS
的installStage()
方法,到這裏就觸發了安裝的第一階段:文件複製
咱們已經知道PackageInstallerSession
經過調用PMS
的installStage()
方法開啓了安裝第一階段,不過整個安裝過程比較複雜,咱們先看看這個過程的序列圖:
installStage()
咱們繼續從installStage()
來分析,代碼以下:
void installStage(String packageName, File stagedDir, IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, String installerPackageName, int installerUid, UserHandle user, PackageParser.SigningDetails signingDetails) {
......
final Message msg = mHandler.obtainMessage(INIT_COPY);
......
final InstallParams params = new InstallParams(origin, null, observer,
sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
verificationInfo, user, sessionParams.abiOverride,
sessionParams.grantedRuntimePermissions, signingDetails, installReason);
......
msg.obj = params;
......
mHandler.sendMessage(msg);
}
複製代碼
installStage()
方法主要進行了:
InstallParams
對象
InstallParams
是安裝過程當中的主要數據結構Android
把安裝過程拆分,把調用過程的參數數據保存到InstallParams
中Message
消息
msg.what
爲INIT_COPY
msg.obj
爲InstallParams
對象先來簡單瞭解下InstallParams
的類:
上圖中的類都是PMS的內部類
InstallParams
繼承自HandlerParams
,用來記錄安裝應用的參數。InstallParams
中有一個成員變量mArgs
,是一個抽象類型InstallArgs
,主要是用來執行APK
的複製,真正的實現類包括:
FileInstallArgs
:用來完成非ASEC
應用的安裝
asec
全稱是Android Secure External Cache
MoveInstallArgs
:用來完成已安裝應用的移動安裝簡單瞭解完InstallParams
,咱們繼續看INIT_COPY
消息的處理過程:
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
int idx = mPendingInstalls.size();
......
if (!mBound) {
// 綁定 DefaultContainerService
if (!connectToService()) {
params.serviceError();
......
return;
} else {
// 鏈接成功,將安裝信息保存到 mPendingInstalls 的尾部
mPendingInstalls.add(idx, params);
}
} else {
// 若是已經綁定服務,直接插入到集合尾部
mPendingInstalls.add(idx, params);
if (idx == 0) {
// 若是插入前的集合爲空
// 直接發送 MCS_BOUND 消息
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
break;
}
複製代碼
整個過程比較簡潔:
connectToService()
去綁定和啓動DefaultContainerService
服務。
onServiceConnected()
返回DefaultContainerConnection
中能夠看到,當服務鏈接成功後,會發送MSC_BOUNF
消息class DefaultContainerConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
final IMediaContainerService imcs = IMediaContainerService.Stub
.asInterface(Binder.allowBlocking(service));
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
}
}
複製代碼
mPendingInstalls
列表中。
mPendingInstalls
進行排隊MSC_BOUND
消息那麼,重點又轉移到了MCS_BOUND
的消息處理,代碼以下:
case MCS_BOUND: {
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
}
if (mContainerService == null) {
if (!mBound) {
// DefaultContainerService bind失敗的狀況
for (HandlerParams params : mPendingInstalls) {
params.serviceError();
......
}
mPendingInstalls.clear();
} else {
Slog.w(TAG, "Waiting to connect to media container service");
}
} else if (mPendingInstalls.size() > 0) {
// 取出頭部安裝參數
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
......
if (params.startCopy()) {// 執行安裝
// 已執行成功,移除頭部元素
if (mPendingInstalls.size() > 0) {
mPendingInstalls.remove(0);
}
if (mPendingInstalls.size() == 0) {
if (mBound) {
......
// 若是集合中沒有須要安裝的信息了,發送MCS_UNBIND延時消息進行解綁
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
sendMessageDelayed(ubmsg, 10000);
}
} else {
// 若是集合中還存在安裝信息,發送 MCS_BOUND 消息,繼續處理
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
}
}
break;
}
複製代碼
MCS_BOUND
消息的處理過程就是:
InstallParams
類的startCopy()
方法來執行安裝
mPendingInstalls
中還有安裝信息,就會重複發送MCS_BOUND
消息10s
的MCS_UNBIND
消息
MCS_UNBIND
消息的處理很簡單,收到消息後若是發現mPendingInstalls
中有數據了,則發送MCS_BOUND
消息繼續安裝。DefaultContainerService
鏈接,安裝過程到此結束case MCS_UNBIND: {
if (mPendingInstalls.size() == 0 && mPendingVerification.size() == 0) {
if (mBound) {
// 斷開 DefaultContainerService 鏈接
disconnectService();
}
} else if (mPendingInstalls.size() > 0) {
// 繼續發送執行安裝操做的消息
mHandler.sendEmptyMessage(MCS_BOUND);
}
break;
}
複製代碼
針對startCopy()
方法的處理過程,代碼以下:
private static final int MAX_RETRIES = 4;
final boolean startCopy() {
boolean res;
try {
// 重試超過4次,則推出
if (++mRetries > MAX_RETRIES) {
// 發送 MCS_GIVE_UP 消息取消安裝
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
// 核心方法,執行 copy
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
// 出現異常,發送 MCS_RECONNECT 消息,進行重連
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
// 開始進入第二階段
handleReturnCode();
return res;
}
複製代碼
startCopy()
方法經過調用handleStartCopy()
來完成安裝過程。考慮到安裝過程的不肯定性,startCopy()
主要是進行錯誤處理:
MCS_RECONNECT
消息處理中,會從新綁定DefaultContainerService
startCopy()
會被再次調用。mRetries
中,超過4次,安裝失敗handleStartCopy()
執行成功,startCopy()
會調用handleReturnCode()
來繼續處理handleStartCopy()
方法比較長,簡要代碼以下:
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
......
if (onInt && onSd) {
// 設置安裝路徑異常標誌
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else if (onSd && ephemeral) {
// 設置安裝路徑異常標誌
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
// 獲取安裝包中的 PackageInfoLite 對象
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride);
......
if (!origin.staged && pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
// TODO: focus freeing disk space on the target device
// 若是安裝空間不夠,嘗試釋放cache空間
......
try {
mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
......
} catch (InstallerException e) {
Slog.w(TAG, "Failed to free cache", e);
}
......
if (pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
pkgLite.recommendedInstallLocation
= PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
}
}
......
final InstallArgs args = createInstallArgs(this);
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
......
if (!origin.existing && requiredUid != -1 && isVerificationEnabled(verifierUser.getIdentifier(), installFlags, installerUid)) {
// 此部分是在執行應用的校驗
// 主要經過向待校驗功能的組件發送Intent來完成
......
/* * We don't want the copy to proceed until verification * succeeds, so null out this field. */
mArgs = null;
} else {
// 無需校驗,調用 InstallArgs 的 copyAPK 方法繼續處理
ret = args.copyApk(mContainerService, true);
}
}
mRet = ret;
}
複製代碼
上面handleStartCopy()
方法的主要邏輯是:
getMinimalPackageInfo()
方法確認是否還有足夠的安裝空間,若是安裝空間不夠,會經過mInstaller.freeCache()
來釋放一部分createInstallArgs()
建立InstallArgs
對象,createInstallArgs()
方法中會對InstallParams
的move
成員變量進行判斷
move
不爲空使用MoveInstallArgs
move
爲空使用FileInstallArgs
,咱們重點來看這個apk
進行校驗,這個校驗過程是經過發送Intent.ACTION_PACKAGE_NEEDS_VERIFICATION)
廣播給系統中全部能夠接收該Intent
的應用來完成的InstallArgs
的copyApk()
方法InstallArgs
的copyApk()
方法以下:
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
......
return doCopyApk(imcs, temp);
}
private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
if (origin.staged) {
codeFile = origin.file;
resourceFile = origin.file;
return PackageManager.INSTALL_SUCCEEDED;
}
try {
final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
// 在/data/app路徑下生成臨時文件
final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
Slog.w(TAG, "Failed to create copy file: " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
// 爲臨時文件建立文件描述符 ParcelFileDescriptor
// 主要是爲了將文件序列化,方便經過binder傳輸
final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
@Override
public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid filename: " + name);
}
try {
final File file = new File(codeFile, name);
final FileDescriptor fd = Os.open(file.getAbsolutePath(),
O_RDWR | O_CREAT, 0644);
Os.chmod(file.getAbsolutePath(), 0644);
return new ParcelFileDescriptor(fd);
} catch (ErrnoException e) {
throw new RemoteException("Failed to open: " + e.getMessage());
}
}
};
int ret = PackageManager.INSTALL_SUCCEEDED;
// 使用服務 DefaultContainerService 的 copyPackage 方法複製文件
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
}
// 安裝應用中自帶的 native 動態庫
final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(codeFile);
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
abiOverride);
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
} finally {
IoUtils.closeQuietly(handle);
}
return ret;
}
複製代碼
copyApk()
最後調用了DefaultContainerService
的copyPackage()
方法將應用的文件複製到了/data/app
目錄下。若是應用中還有native
的動態庫,也會把apk中的庫文件提取出來。
執行完copyApk()
方法後,安裝第一階段的工做就完成了,應用安裝到了/data/app
目錄下。咱們接下來看看handleReturnCode()
的內容,也就是第二階段。
第二階段的工做主要是
oat
格式PMS
的數據結構中handleReturnCode()
方法代碼以下:
void handleReturnCode() {
if (mArgs != null) {
processPendingInstall(mArgs, mRet);
}
}
複製代碼
handleReturnCode()
只是調用了processPendingInstall()
方法繼續處理,這個方法的代碼以下:
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
// Queue up an async operation since the package installation may take a little while.
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);
synchronized (mInstallLock) {
installPackageTracedLI(args, res);
}
args.doPostInstall(res.returnCode, res.uid);
}
......
// 將相關數據記錄到 mRunningInstalls 集合中
PostInstallData data = new PostInstallData(args, res);
mRunningInstalls.put(token, data);
...... // 省略備份部分
if (!doRestore) {
Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
}
}
});
}
複製代碼
processPendingInstall()
方法中post
了一個任務:
installPackageTracedLI()
來裝載應用IBackupManager
服務執行備份相關的操做,這部分就不先深刻了,篇幅已通過大。。。POST_INSTALL
消息繼續處理咱們仍是先看下核心方法installPackageTracedLI()
的內容,代碼以下:
private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {
installPackageLI(args, res);
}
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
......
// Result object to be returned
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
res.installerPackageName = installerPackageName;
......
// 建立 APK 解析對象 PackageParser
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
......
final PackageParser.Package pkg;
try {
// 解析 APK
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
} catch (PackageParserException e) {
res.setError("Failed parse during installPackageLI", e);
return;
}
......
// 收集應用的簽名信息
if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
pkg.setSigningDetails(args.signingDetails);
} else {
PackageParser.collectCertificates(pkg, false /* skipVerify */);
}
......
pp = null;
String oldCodePath = null;
boolean systemApp = false;
synchronized (mPackages) {
// Check if installing already existing package
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);
pkgName = pkg.packageName;
replace = true;
}
......// 省略部分新舊應用的屬性比對
}
// 檢查已安裝的應用集合中是否存在該應用的記錄
PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
// 存在同名應用
......// 省略一些本地庫的簽名判斷
// 檢查是否爲系統應用
oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
if (ps.pkg != null && ps.pkg.applicationInfo != null) {
systemApp = (ps.pkg.applicationInfo.flags &
ApplicationInfo.FLAG_SYSTEM) != 0;
}
res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
// 檢查應用中定義的Permission是否被重複定義
int N = pkg.permissions.size();
for (int i = N-1; i >= 0; i--) {
final PackageParser.Permission perm = pkg.permissions.get(i);
final BasePermission bp =
(BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);
......
// 省略部分只要是對系統定義權限和非系統定義權限的區分
// 若是是系統應用定義的權限,則忽略本應用中的定義,而後繼續
// 若是是非系統應用定義的權限,則本次安裝失敗
......
}
}
......
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
"installPackageLI")) {
if (replace) {
......
replacePackageLIF(pkg, parseFlags, scanFlags, args.user,
installerPackageName, res, args.installReason);
} else {
installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName, volumeUuid, res, args.installReason);
}
}
...... // 省略 dexopt 相關操做
}
}
複製代碼
這部分的代碼邏輯和前面介紹的scanDirLI()
很像,不詳細介紹了,總體流程是:
PackageParser
解析了安裝的應用文件
scanDirLI()
中的parsePackage()
同樣replacePackageLIF()
繼續處理installNewPackageLIF()
處理這裏不深刻進行分析了哈,跟蹤完調用過程後其實都會走到addForInitLI()
中
再次吐槽一下
PMS
,package
解析部分的調用是真滴複雜啊,各類條件判斷
咱們繼續看下POST_INSTALL
消息的處理過程:
case POST_INSTALL: {
PostInstallData data = mRunningInstalls.get(msg.arg1);
final boolean didRestore = (msg.arg2 != 0);
mRunningInstalls.delete(msg.arg1);
if (data != null) {
InstallArgs args = data.args;
PackageInstalledInfo parentRes = data.res;
......
// Handle the parent package
handlePackagePostInstall(parentRes, grantPermissions, killApp,
virtualPreload, grantedPermissions, didRestore,
args.installerPackageName, args.observer);
// Handle the child packages
final int childCount = (parentRes.addedChildPackages != null)
? parentRes.addedChildPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i);
handlePackagePostInstall(childRes, grantPermissions, killApp,
virtualPreload, grantedPermissions, false /*didRestore*/,
args.installerPackageName, args.observer);
}
......
}
......
} break;
複製代碼
POST_INSTALL
的消息處理基本上都是在執行handlePackagePostInstall()
方法,handlePackagePostInstall()
方法主要工做包括兩個:
Intent.ACTION_PACKAGE_ADDED
:新應用安裝成功Intent.ACTION_PACKAGE_REPLACED
:應用更新成功APK
安裝完成。例如,Launcher
中須要增長應用的圖標等IPackageInstallObserver2
的onPackageInstalled()
方法通知觀察者
PackageInstaller
在安裝應用時就是經過註冊觀察者來實現結果監聽的關於
PMS
的軟件卸載流程,你們一樣能夠從PackageInstaller
入手,關鍵類是UninstallUninstalling
。這裏就先跳過了,PMS
篇幅佔用時間過久了。。。。。