1. 程式人生 > >Android系統APP安裝流程

Android系統APP安裝流程

更多內容,歡迎關注公眾號:tmac_lover

這篇文章介紹一下Android裡安裝一個apk檔案的完整流程,我們以pm install安裝一個新的app為例介紹。

1. pm命令

當我們使用

pm install -r /sdcard/test.apk

這樣的pm命令來安裝app的時候, 最終呼叫的是Pm.java的runInstall()方法

private int runInstall() {
    ... ...

     mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
        installerPackageName, verificationParams, abi, userId);

    ... ...
}

最後通過binder呼叫PackageManagerService.java的installPackageAsUser()方法,然後就開始真正的進行apk的安裝工作。可以看到,pm install命令進行app安裝的時候,
是不會經過PackageInstaller的。

2. PackageManagerService

installPackageAsUser()方法裡實現apk的安裝主要可以分三個階段。我們先看下總體流程圖:這裡寫圖片描述

2.1 準備階段

在進行app安裝之前,需要先做一些準備工作。

public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
            int
installFlags, String installerPackageName, VerificationParams verificationParams, String packageAbiOverride, int userId) { ... ... final Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName, null
, verificationParams, user, packageAbiOverride, null); mHandler.sendMessage(msg); }

這裡先構造了一個InstallParams物件,這裡存放一些app檔案安裝需要的資訊,比如apk檔案的路徑,安裝時的flags, 以及安裝完成之後的回撥等等。然後通過Handler傳送一條INIT_COPY訊息。

class PackageHandler extends Handler {
    void doHandleMessage(Message msg) {
        switch (msg.what) {
            case INIT_COPY: {
                if (!mBound) {
                    // 第一次安裝app, 會連線DefaultContainerService
                    if (!connectToService()) {
                        params.serviceError();
                        return;
                    } else {
                        // 然後將app安裝請求放到mPendingInstalls裡
                        mPendingInstalls.add(idx, params);
                    }
                } else {
                    // 如果不是第一次安裝app, DefaultContainerService已經是連線狀態,
                    // 直接將app安裝請求放到mPendingInstalls裡
                    mPendingInstalls.add(idx, params);
                    if (idx == 0) {
                        mHandler.sendEmptyMessage(MCS_BOUND);
                    }
                }
                break;
            }
            case MCS_BOUND: {
                // 不管是哪種情況,INIT_COPY之後,一定會再發一個MSC_BOUND的訊息
                // 這裡開始呼叫InstallParams類的startCopy()方法
                if (params.startCopy()) {
                    if (mPendingInstalls.size() > 0) {
                        mPendingInstalls.remove(0);
                    }
                    // 如果mPendingInstalls裡還有等待安裝的app, 依次執行
                    if (mPendingInstalls.size() == 0) {
                        if (mBound) {
                            removeMessages(MCS_UNBIND);
                            Message ubmsg = obtainMessage(MCS_UNBIND);
                            sendMessageDelayed(ubmsg, 10000);
                        }
                    } else {
                        mHandler.sendEmptyMessage(MCS_BOUND);
                    }
                }
            }
        }
    }
}

總結一下,在安裝app之前的準備工作主要有:
1. 構造一個InstallParams物件,這個物件裡包含了apk檔案的資訊和一些安裝相關的引數
2. 傳送INIT_COPY訊息,這時如果沒有連線DefaultContainerService, 先連線這個service;還有就是將apk安裝請求放入mPendingInstalls裡
3. 再發送MCS_BOUND訊息,呼叫InstallParams物件的startCopy()方法開始安裝app;如果有多個app等待安裝,迴圈發MSC_BOUND訊息,執行安裝操作

2.2 handleStartCopy

前面的準備工作最後,呼叫InstallParams物件的startCopy()方法開始app的安裝工作,startCopy()裡分為兩部分,handleStartCopy()和handleReturnCode()。先看下handleStartCopy()流程圖:這裡寫圖片描述

  • 圖中第2步獲取app安裝的基本資訊;先通過parsePackageLite()提取apk包中的資訊,然後依據AndroidManifest.xml裡的”android:installLocation”屬性和apk安裝需要的大小以及目標分割槽的剩餘空間大小來決定app最終是安裝在data分割槽,還是sdcard上。
  • 第6步installLocationPolicy()方法對第2步返回的apk資訊做進一步確認。如果系統中已經安裝過此app, 需要確認新安裝的app版本號是否比之前的高;同時還會根據上一步獲取的安裝位置資訊設定app安裝需要的Flag
  • 第7步copyApk()方法完成將需要安裝的apk檔案拷貝到要安裝的分割槽的一個零時資料夾裡,並重命名為base.apk,比如/data/app/vmdl1309479077.tmp/base.apk下。同時還會將.apk包裡的.so庫拷貝到/data/app/vmdl1309479077.tmp/libs下

所以handleStartCopy()主要的工作就是將要安裝的apk檔案和使用的.so庫拷貝到要安裝的分割槽的零時目錄下

2.3 handleReturnCode

handleReturnCode裡會完成app安裝的剩餘動作,併發送安裝成功的廣播。這裡寫圖片描述

  • 第4步installPackageLI()方法裡完成了安裝app的核心工作,後面專門分析。
  • 第8步通過Handler傳送POST_INSTALL訊息,這個訊息的處理函式如下:
class PackageHandler extends Handler {
    void doHandleMessage(Message msg) {
        switch (msg.what) {
            ... ...

            case POST_INSTALL: {
                ... ...
                // 傳送ACTION_PACKAGE_ADDED這條廣播
                sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                        packageName, extras, null, null, firstUsers);
                if (args.observer != null) {
                    // 如果有回撥的話,呼叫回撥方法, 通知app安裝完成
                    args.observer.onPackageInstalled(res.name, res.returnCode,
                            res.returnMsg, extras);
                }
                ... ...
                break;
            }
        }
    }
}

在收到POST_INSTALL訊息後,主要完成兩條操作:

  • 傳送ACTION_PACKAGE_ADDED廣播
  • 如果在呼叫installPackageAsUser()方法時有傳入IPackageInstallObserver2作為回撥,則呼叫回撥方法報告安裝狀態

接下來看下第4步installPackageLI()方法:這裡寫圖片描述

  • 第2步parsePackage()通過解析apk包和apk包裡的AndroidManifest.xml,解析出所有apk的資訊,放到Package物件中。
  • 第3步collectCertificates()方法對apk檔案進行簽收校驗,確保apk檔案沒有被更改過以及簽名正確。第4步通過collectManifestDigest()方法獲取AndroidManifest.xml的摘要值。
  • 第5步derivePackageAbi()方法通過apk包裡使用的so庫確定app程序的abi,關於abi,可以檢視之前的一篇文章:
  • 第6步performDexOpt()通過socket和installd通訊,將apk包裡的classes.dex轉化為虛擬機器可以直接執行的檔案格式,關於dex優化,可以檢視之前的一篇文章:
  • 第7步doRename()方法將前面handleStartCopy()裡的零時資料夾/data/app/vmdl1309479077更改成/data/app/$(package_name),這個新的資料夾將是app安裝後的最終目錄
  • 第8步installNewPackageLI()將完成一個新的app的安裝,如果是安裝之前已經存在的apk, 則這裡呼叫的是replacePackageLI()

接下來看看installNewPackageLI()方法的流程:這裡寫圖片描述

  • verifySignaturesLP()使用上一步中collectCertificates()裡獲取的簽名信息進行驗證,主要分兩步:一是如果是安裝一個系統中已經存在的同名app, 則和已經存在的app簽名進行對比,保證兩者簽名相同;二是如果系統中已經存在和正在安裝的app有相同UID的app,
    則需要保證安裝app的簽名和已經存在的使用相同UID的app簽名相同。
  • createDataDirsLI()方法建立app的資料目錄,例如: /data/data/${package_name}/
  • adjustCpuAbisForSharedUserLPw()方法保證如果新安裝的app使用了”android:sharedUID”, 它會將它的abi調整成和系統中使用相同uid的其它app一樣的abi
  • 接下來就會在mSettings中寫入正在安裝app的資訊,比如安裝路徑,資料目錄路徑,簽名信息,abi資訊等。最終將這些資訊存在/data/system/packages.xml中。同時還會將AndroidManifest.xml中的四大元件以及許可權等資訊,存到PackageManagerService的對應數組裡,
    這樣以後通過startActivity之類的方法開啟相應的元件時,就可以找到相應的元件是否存在。需要注意的是,這些四大元件以及許可權資訊,只是儲存在記憶體中,每次系統開機時,都會重新掃描apk檔案,然後將它們再次存到記憶體中使用。

3. 小結

關於新安裝一個apk的大概流程就介紹完了;當然Android系統因為要相容各種各樣的情況,關於安裝apk的程式碼實際上比上面分析的更加的複雜,但是無論有多少if else,核心原理和流程還是和上面分析的一樣。希望大家能夠對著程式碼自己弄解清楚。