APK安裝流程詳解11——普通應(yīng)用安裝簡(jiǎn)介

APK安裝流程系列文章整體內(nèi)容如下:

本片文章的主要內(nèi)容如下:

  • 1、概述
  • 2、Android應(yīng)用程序的幾種安裝方式
  • 3、應(yīng)用安裝涉及到的目錄
  • 4、安裝流程概述
  • 5、PackageInstaller.apk與PackageManger
  • 6、普通的APK安裝方式的界面
  • 7、PackageInstallerActivity類(lèi)的安裝流程
  • 8、InstallAppProgress類(lèi)的安裝流程
  • 9、InstallAppProgress中涉及到PackageManager的三個(gè)方法

一、 概述

眾所周知,Android應(yīng)用最終是打包成.apk格式(其實(shí)就是一個(gè)壓縮包),然后安裝至手機(jī)并運(yùn)行的。其中APK是Android Package的縮寫(xiě)。

Android系統(tǒng)在啟動(dòng)的過(guò)程中,會(huì)啟動(dòng)一個(gè)引用程序管理服務(wù)PackageManagerService,這個(gè)服務(wù)負(fù)責(zé)掃描系統(tǒng)中特定的目錄,找到里面的應(yīng)用程序文件,以.apk為后綴的文件,然后對(duì)這些文件進(jìn)行解析,得到引用程序的相關(guān)信息,完成應(yīng)用程序的安裝過(guò)程。應(yīng)用程序管理服務(wù)PackageManagerService安裝應(yīng)用程序的過(guò)程,其實(shí)就是解析應(yīng)用程序配置文件的AndroidManifest.xml的過(guò)程,并從里面得到應(yīng)用程序的相關(guān)信息,例如得到引用程序的組件Activity、Service、Receiver和Content Provider等信息,有了這些信息后,通過(guò)ActivityManagerService這個(gè)服務(wù),我們就可以在系統(tǒng)中正常地使用這些應(yīng)用程序了。

二、Android應(yīng)用程序的幾種安裝方式

Android上應(yīng)用安裝可以分為以下幾種方式:

  • 1、系統(tǒng)安裝:開(kāi)機(jī)的時(shí)候,沒(méi)有安裝界面
  • 2、adb 命令安裝:通過(guò)abd命令行安裝,沒(méi)有安裝界面
  • 3、應(yīng)用市場(chǎng)安裝,這個(gè)要視應(yīng)用的權(quán)限,有系統(tǒng)的權(quán)限無(wú)安裝界面(例如MUI的小米應(yīng)用商店)
  • 4、第三方安裝,有安裝界面,通過(guò)packageinstaller.apk來(lái)處理安裝及卸載的過(guò)程的界面

三、應(yīng)用安裝涉及到的目錄

  • /system/app:系統(tǒng)自帶的應(yīng)用程序,獲得adb root 權(quán)限才能刪除
  • /data/app:用戶(hù)程序安裝的目錄。安裝時(shí)把a(bǔ)pk文件復(fù)制到此目錄
  • /data/data:存放應(yīng)用程序的數(shù)據(jù)
  • /data/dalvik-cache:將apk中的dex文件安裝到dalvik-cache目錄下(dex文件是dalvik虛擬機(jī)的可執(zhí)行文件,當(dāng)然,ART-Android Runtime的可執(zhí)行文件格式為.oat,啟動(dòng)ART時(shí),系統(tǒng)會(huì)執(zhí)行dex文件轉(zhuǎn)換至oat文件)
  • /data/system:該目錄下的packages.xml文件。類(lèi)似于Window的注冊(cè)表,這個(gè)文件是解析apk時(shí)由writeLP()創(chuàng)建的,里面記錄了系統(tǒng)的permissons,以及每個(gè)apk的name,codePath,flag,ts,version,userid等信息,這些信息主要通過(guò)apk的AndroidManifest解析獲取,解析完apk后將更新信息寫(xiě)入這個(gè)文件并保存到flash,下次開(kāi)機(jī)的時(shí)候直接從里面讀取相關(guān)信息并添加到內(nèi)存相關(guān)列表中。當(dāng)有apk升級(jí),安裝或刪除時(shí)會(huì)更新這個(gè)文件。
    -/data/system/package.xml與/data/system/package.list:packages.list指定了應(yīng)用的默認(rèn)存儲(chǔ)位置/data/data/com.xxx.xxx;package.xml中包含了該應(yīng)用申請(qǐng)的權(quán)限、簽名和代碼所在的位置等信息系,并且兩者都有同一個(gè)userld。之所以每個(gè)應(yīng)用都要一個(gè)userId,是因?yàn)锳ndroid在系統(tǒng)設(shè)計(jì)上把每個(gè)應(yīng)用當(dāng)做Linux系統(tǒng)上的一個(gè)用戶(hù)對(duì)待,這樣就可以利用已有的Linux用戶(hù)管理機(jī)制來(lái)設(shè)計(jì)Android應(yīng)用,比如應(yīng)用目錄,應(yīng)用權(quán)限,應(yīng)用進(jìn)程管理等。

四、安裝流程概述

apk的大體流程如下:

  • 第一步:拷貝文件到指定的目錄:
    在Android系統(tǒng)中,apk安裝文件是會(huì)被保存起來(lái)的,默認(rèn)情況下,用戶(hù)安裝的apk首先會(huì)被拷貝到/data/app目錄下,/data/app目錄是用戶(hù)有權(quán)限訪(fǎng)問(wèn)的目錄,在安裝apk的時(shí)候會(huì)自動(dòng)選擇該目錄存放用戶(hù)安裝的文件,而系統(tǒng)出場(chǎng)的apk文件則被放到了/system分區(qū)下,包括/system/app,/system/vendor/app,以及/system/priv-app等等,該分區(qū)只有ROOT權(quán)限的用戶(hù)才能訪(fǎng)問(wèn),這也就是為什么在沒(méi)有Root手機(jī)之前,我們沒(méi)法刪除系統(tǒng)出場(chǎng)的app的原因了。
  • 第二步:解壓縮apk,寶貝文件,創(chuàng)建應(yīng)用的數(shù)據(jù)目錄
    為了加快app的啟動(dòng)速度,apk在安裝的時(shí)候,會(huì)首先將app的可執(zhí)行文件dex拷貝到/data/dalvik-cache目錄,緩存起來(lái)。然后,在/data/data/目錄下創(chuàng)建應(yīng)用程序的數(shù)據(jù)目錄(以應(yīng)用的包名命名),存放在應(yīng)用的相關(guān)數(shù)據(jù),如數(shù)據(jù)庫(kù)、xml文件、cache、二進(jìn)制的so動(dòng)態(tài)庫(kù)等。
  • 第三步:解析apk的AndroidManifest.xml文件

Android系統(tǒng)中,也有一個(gè)類(lèi)似注冊(cè)表的東西,用來(lái)記錄當(dāng)前所有安裝的應(yīng)用的基本信息,每次系統(tǒng)安裝或者卸載了任何apk文件,都會(huì)更新這個(gè)文件。這個(gè)文件位于如下目錄:/data/system/packages.xml。系統(tǒng)在安裝這個(gè)apk的過(guò)程中,會(huì)解析apk的AndroidManifest.xml文件,提取出這個(gè)apk的重要信息寫(xiě)入到packages.xml文件中,這些信息包括:權(quán)限、應(yīng)用包名、APK的安裝位置、版本、userID等等。由此,我們就知道了為什么一些應(yīng)用市場(chǎng)和軟件管理類(lèi)的app能夠很清楚地知道當(dāng)前手機(jī)所安裝的所有app,以及這些app的詳細(xì)信息了。另外一件事就是Linux的用戶(hù)Id和用戶(hù)組Id,以便他們可以獲得合適的運(yùn)行權(quán)限。以上都是由PackageServcieManager完成的,后面我們會(huì)重點(diǎn)介紹PackageServiceManager。

  • 第四步:顯示快捷方式
    如果這些應(yīng)用程序在PackageManagerService服務(wù)注冊(cè)好了,如果我們想要在Android桌米上看到這些應(yīng)用程序,還需要有一個(gè)Home應(yīng)用程序,負(fù)責(zé)從PackageManagerService服務(wù)中把這些安裝好的應(yīng)用程序取出來(lái),并以友好的方式在桌面上展現(xiàn)出來(lái),例如以快捷圖標(biāo)的形式。在Android系統(tǒng)中,負(fù)責(zé)把系統(tǒng)中已經(jīng)安裝的應(yīng)用程序在桌面中展現(xiàn)出來(lái)的Home應(yīng)用就是Launcher了。

五、PackageInstaller.apk與PackageManger

PackageInstaller.apk地址

PackageInstaller/AndroidManifest.xml.png

(一)、PackageInstaller概述

PackagInstaller是安卓上默認(rèn)的應(yīng)用程序,用它來(lái)安裝普通文件。PackageInstaller提供了用戶(hù)界面來(lái)管理應(yīng)用或者包文件。PackageInstaller調(diào)用一個(gè)叫做InstallAppProgress的activity來(lái)獲取用戶(hù)發(fā)出的指令。InstallAppProgress會(huì)請(qǐng)求Package Manager服務(wù),然后通過(guò)installed來(lái)安裝包文件。

installed這個(gè)守護(hù)進(jìn)程的首要角色就是獲取來(lái)自Package Manager服務(wù)的請(qǐng)求,而該請(qǐng)求是通過(guò)Linux套接字/dev/socket/installed獲得的。installed使用管理員權(quán)限執(zhí)行一系列步驟來(lái)安裝APK。

(二)、PackageInstaller內(nèi)容解析

PackageInstaller的結(jié)構(gòu)如下:


PackageInstaller結(jié)構(gòu)1.png
PackageInstaller結(jié)構(gòu)2.png

這里面重點(diǎn)介紹以下兩個(gè)類(lèi)

  • PackageInstallerActivity:主要是檢查各種權(quán)限,展示被安裝應(yīng)用的向相關(guān)信息,最后跳轉(zhuǎn)到實(shí)際安裝應(yīng)用的InstallAppProgress
  • InstallAppProgress:也是進(jìn)行了一系列的操作,最終把安裝交給了PackageManager.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags, installerPackageName, verificationParams, null);

下面我們就來(lái)看看

六、普通的APK安裝方式的界面

普通的APK安裝方式 一般是經(jīng)過(guò)下面的兩個(gè)界面的


image.png

上面的兩個(gè)界面分別是PackageInstallerActivityInstallAppProgress

七、PackageInstallerActivity類(lèi)的安裝流程

(一)、PackageInstallerActivity類(lèi)

/*
 * This activity is launched when a new application is installed via side loading
 * The package is first parsed and the user is notified of parse errors via a dialog.
 * If the package is successfully parsed, the user is notified to turn on the install unknown
 * applications setting. A memory check is made at this point and the user is notified of out
 * of memory conditions if any. If the package is already existing on the device,
 * a confirmation dialog (to replace the existing package) is presented to the user.
 * Based on the user response the package is then installed by launching InstallAppConfirm
 * sub activity. All state transitions are handled in this activity
 */
public class PackageInstallerActivity extends Activity implements OnCancelListener, OnClickListener {
   ...
   ...
}

我們是知道PackageInstallerActivity是一個(gè)Activity并且實(shí)現(xiàn)了OnCancelListener和OnClickListener接口,下面我們來(lái)看一下注釋。

當(dāng)通過(guò)渠道安裝一個(gè)應(yīng)用程序的時(shí)候,會(huì)啟動(dòng)這個(gè)Activity。如果在首次解析這個(gè)安裝包的時(shí)候出現(xiàn)解析錯(cuò)誤,會(huì)通過(guò)對(duì)話(huà)框的形式告訴用戶(hù)。如果首次解析安裝包的時(shí)候,成功解析了,則會(huì)通知用戶(hù)去打開(kāi)"安裝未知應(yīng)用程序設(shè)置"。在啟動(dòng)Activity的時(shí)候會(huì)進(jìn)行內(nèi)存檢查,如果內(nèi)存不足會(huì)通知用戶(hù)。如果這個(gè)應(yīng)用程序已經(jīng)在這個(gè)設(shè)備安裝過(guò)了,則會(huì)向用戶(hù)彈出一個(gè)對(duì)話(huà)框詢(xún)問(wèn)用戶(hù)是否"替換現(xiàn)有應(yīng)用程序的安裝包"。基于用戶(hù)的回應(yīng),然后通過(guò)InstallAppConfirm的子Activity來(lái)安裝應(yīng)用程序。在這Activity中處理所有狀態(tài)的轉(zhuǎn)換。

大家平時(shí)寫(xiě)Activity一般都是先在onCreate方法里面做一些初始化的操作,那我們來(lái)看下PackageInstallerActivity的onCreate里面都做了什么?

(二)、PackageInstallerActivity類(lèi)的onCreate()方法

代碼在PackageInstallerActivity.java 439行

    PackageManager mPm;
    UserManager mUserManager;
    PackageInstaller mInstaller;
    PackageInfo mPkgInfo;
    ApplicationInfo mSourceInfo;

    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
         //第一步
        // 一個(gè)PackageManager對(duì)象,具體用來(lái)執(zhí)行安裝操作
        mPm = getPackageManager();
        // PackageInstaller對(duì)象,在該對(duì)象中包含了安裝APK的基本信息
        mInstaller = mPm.getPackageInstaller();
        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);

        // 第二步
        final Intent intent = getIntent();
        if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
            final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
            final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
            if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
                Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
                finish();
                return;
            }

            mSessionId = sessionId;
            mPackageURI = Uri.fromFile(new File(info.resolvedBaseCodePath));
            mOriginatingURI = null;
            mReferrerURI = null;
        } else {
            mSessionId = -1;
            mPackageURI = intent.getData();
            mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
            mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
        }

       // 第三步
        final boolean unknownSourcesAllowedByAdmin = isUnknownSourcesAllowedByAdmin();
        final boolean unknownSourcesAllowedByUser = isUnknownSourcesEnabled();

        boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);

        //第四步
        mInstallFlowAnalytics = new InstallFlowAnalytics();
        mInstallFlowAnalytics.setContext(this);
        mInstallFlowAnalytics.setStartTimestampMillis(SystemClock.elapsedRealtime());
        mInstallFlowAnalytics.setInstallsFromUnknownSourcesPermitted(unknownSourcesAllowedByAdmin
                && unknownSourcesAllowedByUser);
        mInstallFlowAnalytics.setInstallRequestFromUnknownSource(requestFromUnknownSource);
        mInstallFlowAnalytics.setVerifyAppsEnabled(isVerifyAppsEnabled());
        mInstallFlowAnalytics.setAppVerifierInstalled(isAppVerifierInstalled());
        mInstallFlowAnalytics.setPackageUri(mPackageURI.toString());

        //第五步
        final String scheme = mPackageURI.getScheme();
        if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
            Log.w(TAG, "Unsupported scheme " + scheme);
            setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
            mInstallFlowAnalytics.setFlowFinished(
                    InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME);
            finish();
            return;
        }

        final PackageUtil.AppSnippet as;
        //處理scheme為package的情況
        if ("package".equals(mPackageURI.getScheme())) {
            mInstallFlowAnalytics.setFileUri(false);
            try {
                //獲取package對(duì)應(yīng)的Android應(yīng)用信息PackageInfo如果應(yīng)用名稱(chēng),權(quán)限列表等...
                mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart(),
                        PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES);
            } catch (NameNotFoundException e) {
            }
            //如果無(wú)法獲取PackageInfo,彈出一個(gè)錯(cuò)誤的對(duì)話(huà)框,然后直接退出安裝
            if (mPkgInfo == null) {
                Log.w(TAG, "Requested package " + mPackageURI.getScheme()
                        + " not available. Discontinuing installation");
                showDialogInner(DLG_PACKAGE_ERROR);
                setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
                mInstallFlowAnalytics.setPackageInfoObtained();
                mInstallFlowAnalytics.setFlowFinished(
                        InstallFlowAnalytics.RESULT_FAILED_PACKAGE_MISSING);
                return;
            }
            //創(chuàng)建AppSnipet對(duì)象,該對(duì)象封裝了待安裝Android應(yīng)用的標(biāo)題和圖標(biāo)
            as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
                    mPm.getApplicationIcon(mPkgInfo.applicationInfo));
        } else {
           // 處理scheme為file的情況
            mInstallFlowAnalytics.setFileUri(true);
            // 獲取APK文件的實(shí)際路徑
            final File sourceFile = new File(mPackageURI.getPath());
            // 創(chuàng)建APK文件的分析器 parsed。同時(shí)分析安裝包,后面會(huì)單獨(dú)講解
            PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);

            // Check for parse errors
            if (parsed == null) {
               //如果parsed == null,則說(shuō)明解析出錯(cuò),則彈出對(duì)話(huà)框,并退出安裝
                Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
                showDialogInner(DLG_PACKAGE_ERROR);
                setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
                mInstallFlowAnalytics.setPackageInfoObtained();
                mInstallFlowAnalytics.setFlowFinished(
                        InstallFlowAnalytics.RESULT_FAILED_TO_GET_PACKAGE_INFO);
                return;
            }
            //解析沒(méi)出錯(cuò),生成PackageInfo,這里面包含APK文件的相關(guān)信息
            mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
                    PackageManager.GET_PERMISSIONS, 0, 0, null,
                    new PackageUserState());
            //manifest校驗(yàn)
            mPkgDigest = parsed.manifestDigest;
            // 設(shè)置apk的程序名稱(chēng)和圖標(biāo),這是另一種創(chuàng)建AppSnippet的方式
            as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
        }
        mInstallFlowAnalytics.setPackageInfoObtained();

        //set view
        // 第六步
        setContentView(R.layout.install_start);
        mInstallConfirm = findViewById(R.id.install_confirm_panel);
        mInstallConfirm.setVisibility(View.INVISIBLE);
        PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);

        mOriginatingUid = getOriginatingUid(intent);

         // 第七步
        // Block the install attempt on the Unknown Sources setting if necessary.
        // 如果必須要禁止來(lái)自未知來(lái)源的安裝
        if (!requestFromUnknownSource) {
             //初始化操作
            initiateInstall();
            return;
        }

       // 第八步
        // If the admin prohibits it, or we're running in a managed profile, just show error
        // and exit. Otherwise show an option to take the user to Settings to change the setting.
        // 未知來(lái)源檢查,如果admin禁止則直接提示錯(cuò)誤退出。否則顯示選項(xiàng)提示用戶(hù)去設(shè)置修改上修改設(shè)置
        final boolean isManagedProfile = mUserManager.isManagedProfile();
        if (!unknownSourcesAllowedByAdmin
                || (!unknownSourcesAllowedByUser && isManagedProfile)) {
            showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
            mInstallFlowAnalytics.setFlowFinished(
                    InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
        } else if (!unknownSourcesAllowedByUser) {
            // Ask user to enable setting first
            showDialogInner(DLG_UNKNOWN_SOURCES);
            mInstallFlowAnalytics.setFlowFinished(
                    InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
        } else {
            initiateInstall();
        }
    }

代碼很多,那我們來(lái)看重點(diǎn)

  • 第一步: 給mPm、mInstaller、mUserManager進(jìn)行初始化。
  • 第二步: 獲取mSessionId、mPackageURI、mOriginatingURI、mReferrerURI 這四個(gè)是重要參數(shù)
  • 第三步: 判斷是否是來(lái)自未知來(lái)源的包,看是否是非官網(wǎng)下載的app,這里面有三個(gè)判斷依次為:
    • isUnknownSourcesAllowedByAdmin():設(shè)備管理員是否限制來(lái)自未知來(lái)源的安裝
    • isUnknownSourcesEnabled():在“設(shè)置”中用戶(hù)是否啟用未知來(lái)源
    • isInstallRequestFromUnknownSource(Intent):安裝請(qǐng)求是否來(lái)自一個(gè)未知的源
  • 第四步: 創(chuàng)建mInstallFlowAnalytics對(duì)象,并進(jìn)行一些字段的賦值InstallFlowAnalytics.java是用來(lái)安裝軟件包的分析工具。
  • 第五步: 檢查scheme是否支持,如果不支持則直接結(jié)束,如果支持scheme,這里面又分為兩種情況
    • 處理scheme為package的情況
    • 處理scheme為file的情況
      無(wú)論是上面的哪種情況,都是要首先獲取PackageInfo對(duì)象,如果scheme是package的情況下是直接調(diào)用PackageManager. getPackageInfo()方法獲取的;如果scheme是file則是通過(guò)APK的實(shí)際路徑即mPackageURI.getPath()來(lái)構(gòu)造一個(gè)File,然后通過(guò) PackageParser.generatePackageInfo()方法來(lái)獲取的。然后創(chuàng)建AppSnippet對(duì)象,AppSnippet是PackageUtil的靜態(tài)內(nèi)部類(lèi),內(nèi)部封裝了icon和label。注意不同sheme
  • 第六步: 設(shè)置activity的主界面
  • 第七步: 判斷是限制未知來(lái)源的安裝包
  • 第八步: 進(jìn)行未知來(lái)源的檢查
    大家如果仔細(xì)閱讀源碼的話(huà),就會(huì)知道,無(wú)論是否限制未知來(lái)源的安裝包,如果沒(méi)有問(wèn)題都會(huì)調(diào)用initiateInstall();來(lái)進(jìn)行初始化操作

OK,PackageInstallerActivity類(lèi)的onCreate()方法分析完畢,為了讓大家更好的理解,下面講解下這里面涉及到幾個(gè)核心方法:

  • PackageUtil.getPackageInfo(sourceFile)
  • initiateInstall()
1、PackageUtil.getPackageInfo(sourceFile)方法解析

代碼在PackageUtil.java

    /**
     * Utility method to get package information for a given {@link File}
     */
    public static PackageParser.Package getPackageInfo(File sourceFile) {
        final PackageParser parser = new PackageParser();
        try {
            PackageParser.Package pkg = parser.parseMonolithicPackage(sourceFile, 0);
            parser.collectManifestDigest(pkg);
            return pkg;
        } catch (PackageParserException e) {
            return null;
        }
    }

我們看到這個(gè)方法里面主要就是new了一個(gè)PackageParser對(duì)象
然后調(diào)用parser.parseMonolithicPackage(sourceFile, 0);方法和 parser.collectManifestDigest(pkg);方法,如果不拋異常就直接返回 PackageParser.Package對(duì)象,如果拋異常則直接返回null。看來(lái)上面這兩個(gè)方法很重,關(guān)于parser.parseMonolithicPackage(sourceFile, 0);方法請(qǐng)參考APK安裝流程詳解6——PackageParser解析APK(上)四、PackageParse#parseMonolithicPackage(File, int)方法解析。

2、initiateInstall()方法解析

代碼在PackageInstallerActivity.java 400行

    private void initiateInstall() {
        String pkgName = mPkgInfo.packageName;
        // Check if there is already a package on the device with this name
        // but it has been renamed to something else.
        //第一步
        String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
        if (oldName != null && oldName.length > 0 && oldName[0] != null) {
            pkgName = oldName[0];
            mPkgInfo.packageName = pkgName;
            mPkgInfo.applicationInfo.packageName = pkgName;
        }
        // Check if package is already installed. display confirmation dialog if replacing pkg
         // 第二步
        try {
            // This is a little convoluted because we want to get all uninstalled
            // apps, but this may include apps with just data, and if it is just
            // data we still want to count it as "installed".
            // 獲取設(shè)備上的殘存數(shù)據(jù),并且標(biāo)記為“installed”,實(shí)際上已經(jīng)被卸載的應(yīng)用
            mAppInfo = mPm.getApplicationInfo(pkgName,
                    PackageManager.GET_UNINSTALLED_PACKAGES);
            if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
如果應(yīng)用是被卸載的,但是又是被標(biāo)識(shí)成安裝過(guò)的,則認(rèn)為是新安裝
                 // 如果應(yīng)用是被卸載的,但是又是被標(biāo)識(shí)成安裝過(guò)的,則認(rèn)為是新安裝
                mAppInfo = null;
            }
        } catch (NameNotFoundException e) {
            mAppInfo = null;
        }

        mInstallFlowAnalytics.setReplace(mAppInfo != null);
        mInstallFlowAnalytics.setSystemApp(
                (mAppInfo != null) && ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0));
        // 第三步
        startInstallConfirm();
    }

initiateInstall()方法里面主要做了三件事

  • 第一步:檢查設(shè)備是否有一個(gè)現(xiàn)在不同名,但是曾經(jīng)是相同的包名,即是否是同名安裝,如果是則后續(xù)是替換安裝。
  • 第二步:檢查設(shè)備上是否已經(jīng)安裝了這個(gè)安裝包,如果是,后面是替換安裝
  • 第三步:調(diào)用startInstallConfirm()這個(gè)方法是安裝的核心代碼。

下面我們就來(lái)看下startInstallConfirm()方法里面的具體實(shí)現(xiàn)

①、startInstallConfirm()方法解析

代碼在PackageInstallerActivity.java 114行

    private void startInstallConfirm() {
        TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);
        tabHost.setup();
        ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
        TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
        adapter.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
            @Override
            public void onTabChanged(String tabId) {
                if (TAB_ID_ALL.equals(tabId)) {
                    mInstallFlowAnalytics.setAllPermissionsDisplayed(true);
                } else if (TAB_ID_NEW.equals(tabId)) {
                    mInstallFlowAnalytics.setNewPermissionsDisplayed(true);
                }
            }
        });
        // If the app supports runtime permissions the new permissions will
        // be requested at runtime, hence we do not show them at install.

         // 根據(jù)sdk版本來(lái)判斷app是否支持運(yùn)行時(shí)權(quán)限,這里會(huì)顯示運(yùn)行時(shí)權(quán)限
        boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion
                >= Build.VERSION_CODES.M;

       //顯示權(quán)限列表的變量,true顯示權(quán)限列表,false 未顯示權(quán)限列表
        boolean permVisible = false;
        mScrollView = null;
        mOkCanInstall = false;
        int msg = 0;
 //perms這個(gè)對(duì)象包括了該應(yīng)用的用戶(hù)的uid以及相應(yīng)的一些權(quán)限,以及權(quán)限組信息
         // perms這個(gè)對(duì)象包括了該應(yīng)用的用戶(hù)的uid以及相應(yīng)的一些權(quán)限,以及權(quán)限組的信息
        AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
        // 獲取隱私相關(guān)權(quán)限的數(shù)量
        final int N = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL);
        //判斷是否為已經(jīng)安裝過(guò)的應(yīng)用
        if (mAppInfo != null) {
           // 如果已經(jīng)安裝過(guò)則繼續(xù)判斷是否為系統(tǒng)應(yīng)用
            msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
                    ? R.string.install_confirm_question_update_system
                    : R.string.install_confirm_question_update;
            
            // 用來(lái)顯示權(quán)限列表的scrollview
            mScrollView = new CaffeinatedScrollView(this);
            // 如果顯示的內(nèi)容超過(guò)了mScrollView,則就會(huì)折疊可以滾動(dòng) 
            mScrollView.setFillViewport(true);
            boolean newPermissionsFound = false;
            if (!supportsRuntimePermissions) {
                //針對(duì)更新應(yīng)用程序相對(duì)于舊版本而判斷是否加入新的權(quán)限
                newPermissionsFound =
                        (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
                mInstallFlowAnalytics.setNewPermissionsFound(newPermissionsFound);
                if (newPermissionsFound) {
                     //將新的權(quán)限列表視頻添加到滾動(dòng)視圖中
                    permVisible = true;
                    mScrollView.addView(perms.getPermissionsView(
                            AppSecurityPermissions.WHICH_NEW));
                }
            }
            if (!supportsRuntimePermissions && !newPermissionsFound) {
                // 沒(méi)有設(shè)置任何權(quán)限,只顯示應(yīng)用程序名稱(chēng)和圖標(biāo)
                LayoutInflater inflater = (LayoutInflater)getSystemService(
                        Context.LAYOUT_INFLATER_SERVICE);
                TextView label = (TextView)inflater.inflate(R.layout.label, null);
                label.setText(R.string.no_new_perms);
                mScrollView.addView(label);
            }
            adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(
                    getText(R.string.newPerms)), mScrollView);
        } else  {
            // 應(yīng)用沒(méi)有被安裝過(guò),則將相應(yīng)的控件隱藏
            findViewById(R.id.tabscontainer).setVisibility(View.GONE);
            findViewById(R.id.divider).setVisibility(View.VISIBLE);
        }
        // 如果至少設(shè)置了一個(gè)權(quán)限
        if (!supportsRuntimePermissions && N > 0) {
            permVisible = true;
            LayoutInflater inflater = (LayoutInflater)getSystemService(
                    Context.LAYOUT_INFLATER_SERVICE);
            //解析權(quán)限列表的視圖
            View root = inflater.inflate(R.layout.permissions_list, null);
            if (mScrollView == null) {
                mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview);
            }
           // 添加到權(quán)限列表的視圖
            ((ViewGroup)root.findViewById(R.id.permission_list)).addView(
                       perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL));
            adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(
                    getText(R.string.allPerms)), root);
        }
        mInstallFlowAnalytics.setPermissionsDisplayed(permVisible);
        
        if (!permVisible) {
            // 如果不顯示權(quán)限列表
            if (mAppInfo != null) {
                // 如果是更新安裝包,并且沒(méi)有任何權(quán)限要求
                // This is an update to an application, but there are no
                // permissions at all.
                //判斷是否是系統(tǒng)應(yīng)用來(lái)設(shè)置布局文件 
                msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
                        ? R.string.install_confirm_question_update_system_no_perms
                        : R.string.install_confirm_question_update_no_perms;
            } else {
                // 是新安裝的app并且沒(méi)有權(quán)限列表
                // This is a new application with no permissions.
                msg = R.string.install_confirm_question_no_perms;
            }
            //設(shè)置相應(yīng)的UI
            tabHost.setVisibility(View.GONE);
            mInstallFlowAnalytics.setAllPermissionsDisplayed(false);
            mInstallFlowAnalytics.setNewPermissionsDisplayed(false);
            findViewById(R.id.filler).setVisibility(View.VISIBLE);
            findViewById(R.id.divider).setVisibility(View.GONE);
            mScrollView = null;
        }
        if (msg != 0) {
            ((TextView)findViewById(R.id.install_confirm_question)).setText(msg);
        }
        mInstallConfirm.setVisibility(View.VISIBLE);
        //這個(gè)是關(guān)鍵的控件,即點(diǎn)擊安裝button
        mOk = (Button)findViewById(R.id.ok_button);
       //這個(gè)是關(guān)鍵的控件,即點(diǎn)擊取消button
        mCancel = (Button)findViewById(R.id.cancel_button);
        mOk.setOnClickListener(this);
        mCancel.setOnClickListener(this);
        if (mScrollView == null) {
            // There is nothing to scroll view, so the ok button is immediately
            // set to install.
            mOk.setText(R.string.install);
            mOkCanInstall = true;
        } else {
            mScrollView.setFullScrollAction(new Runnable() {
                @Override
                public void run() {
                    mOk.setText(R.string.install);
                    mOkCanInstall = true;
                }
            });
        }
    }

這個(gè)方法其實(shí)主要是根據(jù)不同的情況來(lái)設(shè)置相應(yīng)的UI,主要是將安裝包分為新安裝和更新安裝,在更新安裝里面又分為系統(tǒng)應(yīng)用和非系統(tǒng)應(yīng)用,然后根據(jù)不同的情況來(lái)顯示不同的UI,UI這塊主要是通過(guò)getPermissionsView方法來(lái)獲取不同的權(quán)限View。

PS:AppSecurityPermissions.WHICH_NEW:新加入的權(quán)限

這個(gè)重點(diǎn)說(shuō)下mOk這個(gè)Button,因?yàn)楹竺嬖蹅凕c(diǎn)擊"安裝"按鈕的流程就是從這個(gè)按鈕開(kāi)始的。

(三)、PackageInstallerActivity類(lèi)中點(diǎn)擊"安裝"的流程詳解

由于PackageInstallerActivity實(shí)現(xiàn)了OnClickListener接口,所以點(diǎn)擊事件我們直接找onClick(View)方法即可

1、onClick(View v)方法解析

代碼在PackageInstallerActivity.java 114行

    public void onClick(View v) {
        if (v == mOk) {
            if (mOkCanInstall || mScrollView == null) {
                mInstallFlowAnalytics.setInstallButtonClicked();
                if (mSessionId != -1) {
                   //如果原來(lái)是確認(rèn)權(quán)限請(qǐng)求則賦予安裝權(quán)限則退出
                    mInstaller.setPermissionsResult(mSessionId, true);

                    // We're only confirming permissions, so we don't really know how the
                    // story ends; assume success.
                    mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(
                            PackageManager.INSTALL_SUCCEEDED);
                    finish();
                } else {
                    startInstall();
                }
            } else {
                mScrollView.pageScroll(View.FOCUS_DOWN);
            }
        } else if(v == mCancel) {
            // Cancel and finish
            setResult(RESULT_CANCELED);
            if (mSessionId != -1) {
                mInstaller.setPermissionsResult(mSessionId, false);
            }
            mInstallFlowAnalytics.setFlowFinished(
                    InstallFlowAnalytics.RESULT_CANCELLED_BY_USER);
            finish();
        }
    }

我們重點(diǎn)看v == mOk的情況,里面其實(shí)就是做了兩件事

  • 1、判斷是否可以安裝,mScrollVieww是否為空
  • 2、如果可以安裝,那么調(diào)用startInstall()方法

那下面我們來(lái)看下startInstall()的具體實(shí)現(xiàn)

2、startInstall()方法解析

代碼在PackageInstallerActivity.java 683行

    private void startInstall() {
        // Start subactivity to actually install the application
        Intent newIntent = new Intent();

         //帶上安裝包的applicationInfo
        newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
                mPkgInfo.applicationInfo);
    
        // 帶上安裝包的URI
        newIntent.setData(mPackageURI);

        //設(shè)置目標(biāo)類(lèi)
        newIntent.setClass(this, InstallAppProgress.class);
        
        //帶上安裝包的mPkgDigest
newIntent.putExtra(InstallAppProgress.EXTRA_MANIFEST_DIGEST, mPkgDigest);

         // 帶上mInstallFlowAnalytics 
        newIntent.putExtra(
                InstallAppProgress.EXTRA_INSTALL_FLOW_ANALYTICS, mInstallFlowAnalytics);
        String installerPackageName = getIntent().getStringExtra(
                Intent.EXTRA_INSTALLER_PACKAGE_NAME);
        if (mOriginatingURI != null) {

            //帶上安裝包的mOriginatingURI
            newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
        }
        if (mReferrerURI != null) {
            //帶上安裝包的mReferrerURI
            newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
        }
        if (mOriginatingUid != VerificationParams.NO_UID) {
             //帶上安裝包的mOriginatingUid 這個(gè)uid不是安裝應(yīng)用的uid
            newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
        }
        if (installerPackageName != null) {
            newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
                    installerPackageName);
        }
        if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
            newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
            newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
        }
        if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
        startActivity(newIntent);
        finish();
    }

上面代碼很簡(jiǎn)單,主要是就是構(gòu)造在一個(gè)Intent,并且傳遞必須要的數(shù)據(jù),

可以看到在startInstall方法中,主要構(gòu)造一個(gè)intent,并且將安裝包信息封裝到intent中,然后跳轉(zhuǎn)到InstallAppProgress類(lèi)

下面我們就來(lái)看下InstallAppProgress這個(gè)類(lèi)的安裝流程

八、InstallAppProgress類(lèi)的安裝流程

(一)、InstallAppProgress類(lèi)簡(jiǎn)介

/**
 * This activity corresponds to a download progress screen that is displayed
 * when the user tries
 * to install an application bundled as an apk file. The result of the application install
 * is indicated in the result code that gets set to the corresponding installation status
 * codes defined in PackageManager. If the package being installed already exists,
 * the existing package is replaced with the new one.
 */
public class InstallAppProgress extends Activity implements View.OnClickListener, OnCancelListener {
        ...
        ...
}

通過(guò)上面代碼我們知道InstallAppProgress其實(shí)是一個(gè)Activity并且實(shí)現(xiàn)了OnClickListener和OnCancelListener接口

為了讓大家更好的理解這個(gè)類(lèi),我們還是看一下這個(gè)類(lèi)的注釋?zhuān)业姆g如下(不喜勿噴):

這個(gè)Activity是負(fù)責(zé)當(dāng)用戶(hù)嘗試安裝APK應(yīng)用程序時(shí)的進(jìn)度顯示的Activity。關(guān)于這個(gè)應(yīng)用程序的安裝結(jié)果的狀態(tài)碼是和PackageManager里面定義的安裝狀態(tài)碼一一映射的。如果正在安裝的應(yīng)用已經(jīng)在設(shè)備上存在了,則新的應(yīng)用程序?qū)?huì)替換掉老的應(yīng)用程序。

既然它是一個(gè)Activity,那么我們先看下他的onCreate()方法

(二)、InstallAppProgress的onCreate(Bundle)方法

代碼在InstallAppProgress.java 167行

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        //第一步
        Intent intent = getIntent();
        mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
        mInstallFlowAnalytics = intent.getParcelableExtra(EXTRA_INSTALL_FLOW_ANALYTICS);
        mInstallFlowAnalytics.setContext(this);
        mPackageURI = intent.getData();
      
        //第二步
        final String scheme = mPackageURI.getScheme();
        if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
            mInstallFlowAnalytics.setFlowFinished(
                    InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME);
            throw new IllegalArgumentException("unexpected scheme " + scheme);
        }
        
        // 第三步 
        initView();
    }

通過(guò)上面代碼我們知道onCreate方法里面主要做了3三件事

  • 首先:初始化數(shù)據(jù),把Intent里面的數(shù)據(jù)取出
  • 其次:做scheme數(shù)據(jù)過(guò)濾,只支持scheme為file或者package格式的
  • 最后:最后調(diào)用initView()方法
1、initView()方法詳解

代碼在InstallAppProgress.java 226行

    public void initView() {
         // 第一步
        setContentView(R.layout.op_progress);

         // 第二步
        // 安裝模式 分為安裝和更新
        int installFlags = 0;
        PackageManager pm = getPackageManager();

        // 第三步
        try {
            PackageInfo pi = pm.getPackageInfo(mAppInfo.packageName,
                    PackageManager.GET_UNINSTALLED_PACKAGES);
            if(pi != null) {
                installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
            }
        } catch (NameNotFoundException e) {
        }

         // 第四步
        if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) {
            Log.w(TAG, "Replacing package:" + mAppInfo.packageName);
        }

        // 第五步
        final PackageUtil.AppSnippet as;
        if ("package".equals(mPackageURI.getScheme())) {
            as = new PackageUtil.AppSnippet(pm.getApplicationLabel(mAppInfo),
                    pm.getApplicationIcon(mAppInfo));
        } else {
            final File sourceFile = new File(mPackageURI.getPath());
            as = PackageUtil.getAppSnippet(this, mAppInfo, sourceFile);
        }

        // 第六步
        mLabel = as.label;
        PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
        mStatusTextView = (TextView)findViewById(R.id.center_text);
        mStatusTextView.setText(R.string.installing);
        mExplanationTextView = (TextView) findViewById(R.id.center_explanation);
        mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
        mProgressBar.setIndeterminate(true);
        // Hide button till progress is being displayed
        mOkPanel = (View)findViewById(R.id.buttons_panel);
        mDoneButton = (Button)findViewById(R.id.done_button);
        mLaunchButton = (Button)findViewById(R.id.launch_button);
        mOkPanel.setVisibility(View.INVISIBLE);

        // 第七步
        String installerPackageName = getIntent().getStringExtra(
                Intent.EXTRA_INSTALLER_PACKAGE_NAME);
        Uri originatingURI = getIntent().getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
        Uri referrer = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
        int originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
                VerificationParams.NO_UID);
        ManifestDigest manifestDigest = getIntent().getParcelableExtra(EXTRA_MANIFEST_DIGEST);
        VerificationParams verificationParams = new VerificationParams(null, originatingURI,
                referrer, originatingUid, manifestDigest);

        // 第八步
        PackageInstallObserver observer = new PackageInstallObserver();

        // 第九步
        if ("package".equals(mPackageURI.getScheme())) {
            try {
                pm.installExistingPackage(mAppInfo.packageName);
                observer.packageInstalled(mAppInfo.packageName,
                        PackageManager.INSTALL_SUCCEEDED);
            } catch (PackageManager.NameNotFoundException e) {
                observer.packageInstalled(mAppInfo.packageName,
                        PackageManager.INSTALL_FAILED_INVALID_APK);
            }
        } else {
            pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
                    installerPackageName, verificationParams, null);
        }
    }

上面代碼還是比較簡(jiǎn)潔的,大體上將initView()分為九個(gè)步驟,如下:

  • 第一步:設(shè)置當(dāng)前Activity的布局文件
  • 第二步:獲取PackageManager對(duì)象
  • 第三步:獲取PackgeInfo對(duì)象,這里使用pm.getPackageInfo()方法來(lái)獲取,主要是判斷待安裝的應(yīng)用程序是否已經(jīng)安裝,因?yàn)槿绻呀?jīng)安裝了,則返回PackgeInfo對(duì)象,則安裝模式設(shè)為更新模式,如果沒(méi)有安裝,則返回null
  • 第四步:如果是替換安裝則打印日志
  • 第五步:根據(jù)不同的scheme來(lái)給AppSnippet進(jìn)行賦值,如果scheme為package則意味著更新應(yīng)用程序;scheme為file則意味著是新安裝應(yīng)用程序。
  • 第六步:獲取布局文件中的控件
  • 第七步:從Intent中獲取相應(yīng)的數(shù)據(jù)信息,為下一步做準(zhǔn)備
  • 第八步:創(chuàng)建安裝的監(jiān)聽(tīng)器對(duì)象
  • 第九步:根據(jù)不用的scheme來(lái)進(jìn)行不同安裝模式下的安裝操作

這個(gè)方法里面涉及到三個(gè)重要內(nèi)容如下

  • 1、PackageInstallObserver
  • 2、PackageManager的installExistingPackage(String)方法
  • 3、PackageManager的installPackageWithVerificationAndEncryption(PackageInstallObserver , int , String ,VerificationParams , ContainerEncryptionParams )方法
(1)、PackageInstallObserver類(lèi)詳解
(1.1)、PackageInstallObserver類(lèi)源碼

代碼在InstallAppProgress.java 218行

    class PackageInstallObserver extends IPackageInstallObserver.Stub {
        public void packageInstalled(String packageName, int returnCode) {
            Message msg = mHandler.obtainMessage(INSTALL_COMPLETE);
            msg.arg1 = returnCode;
            mHandler.sendMessage(msg);
        }
    }

通過(guò)上面代碼我們知道
PackageInstallObserver其實(shí)是繼承IPackageInstallObserver.Stub 類(lèi)的,在packageInstalled(String, int)方法里面其實(shí)向mHandler發(fā)送了一個(gè)Message

PS:注意這個(gè)Message的what值是INSTALL_COMPLETE,Message的arg1是安裝結(jié)果的code。

那我們看下這個(gè)mHandler的里面是怎么操作的

(1.2)、InstallAppProgress類(lèi)中的Handler對(duì)象mHandler解析

代碼在InstallAppProgress.java 76行

    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case INSTALL_COMPLETE:
                     //記錄安裝結(jié)果
                    mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(msg.arg1);
                    
                    // 第一步
                    // 判斷是否需要安裝結(jié)束后立即結(jié)束當(dāng)前界面
                    if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
                        
                        Intent result = new Intent();
                        result.putExtra(Intent.EXTRA_INSTALL_RESULT, msg.arg1);
                        setResult(msg.arg1 == PackageManager.INSTALL_SUCCEEDED
                                ? Activity.RESULT_OK : Activity.RESULT_FIRST_USER,
                                        result);
                        finish();
                        return;
                    }
                     
                    // 第二步
                    // Update the status text
                    mProgressBar.setVisibility(View.INVISIBLE);
                    // Show the ok button
                    int centerTextLabel;
                    int centerExplanationLabel = -1;
                    LevelListDrawable centerTextDrawable =
                            (LevelListDrawable) getDrawable(R.drawable.ic_result_status);

                     // 第三步 
                    if (msg.arg1 == PackageManager.INSTALL_SUCCEEDED) {
                        // 安裝成功
                        mLaunchButton.setVisibility(View.VISIBLE);
                        centerTextDrawable.setLevel(0);
                        centerTextLabel = R.string.install_done;
                        // Enable or disable launch button
                        // 獲取應(yīng)用程序啟動(dòng)的Intent
                        mLaunchIntent = getPackageManager().getLaunchIntentForPackage(
                                mAppInfo.packageName);
                        boolean enabled = false;
                         // 判斷應(yīng)用程序啟動(dòng)Intent是否可用
                        if(mLaunchIntent != null) {
                            List<ResolveInfo> list = getPackageManager().
                                    queryIntentActivities(mLaunchIntent, 0);
                            if (list != null && list.size() > 0) {
                                enabled = true;
                            }
                        }
                        if (enabled) {
                            mLaunchButton.setOnClickListener(InstallAppProgress.this);
                        } else {
                            mLaunchButton.setEnabled(false);
                        }
                    } else if (msg.arg1 == PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE){
                        // 由于剩余空間不足導(dǎo)致安裝失敗
                        showDialogInner(DLG_OUT_OF_SPACE);
                        return;
                    } else {
                        // 安裝失敗
                        // Generic error handling for all other error codes.
                        centerTextDrawable.setLevel(1);
                        centerExplanationLabel = getExplanationFromErrorCode(msg.arg1);
                        centerTextLabel = R.string.install_failed;
                        mLaunchButton.setVisibility(View.INVISIBLE);
                    }

                    // 第四步
                    if (centerTextDrawable != null) {
                    centerTextDrawable.setBounds(0, 0,
                            centerTextDrawable.getIntrinsicWidth(),
                            centerTextDrawable.getIntrinsicHeight());
                        mStatusTextView.setCompoundDrawablesRelative(centerTextDrawable, null,
                                null, null);
                    }
                    mStatusTextView.setText(centerTextLabel);
                    if (centerExplanationLabel != -1) {
                        mExplanationTextView.setText(centerExplanationLabel);
                        mExplanationTextView.setVisibility(View.VISIBLE);
                    } else {
                        mExplanationTextView.setVisibility(View.GONE);
                    }
                    mDoneButton.setOnClickListener(InstallAppProgress.this);
                    mOkPanel.setVisibility(View.VISIBLE);
                    break;
                default:
                    break;
            }
        }
    };

由于Message的what值為INSTALL_COMPLETE,我們只需要關(guān)心case值為INSTALL_COMPLETE的情況(不過(guò)貌似沒(méi)有別的case),case的值為INSTALL_COMPLETE的內(nèi)容主要分為四個(gè)步驟,如下:

  • 第一步:判斷是否有安裝結(jié)束后(不論成功或者失敗),立即離開(kāi)當(dāng)前的需求,如果有這個(gè)要求,則完成結(jié)束后,立即返回,如果是安裝成功則resultCode為PackageManager.INSTALL_SUCCEEDED,如果失敗resultCode為Activity.RESULT_FIRST_USER。
  • 第二步:更變UI,并且給centerTextDrawable賦值
  • 第三步:根據(jù)安裝結(jié)果的code(是msg.arg1)來(lái)更新UI及后續(xù)的操作,這里面分為三種情況:
    • 安裝結(jié)果code為PackageManager.INSTALL_SUCCEEDED:
      表示安裝成功,首先進(jìn)行UI更新,然后通過(guò)調(diào)用getPackageManager().getLaunchIntentForPackage(mAppInfo.packageName);來(lái)獲取這個(gè)應(yīng)用程序的啟動(dòng)Intent,接著判斷這個(gè)Intent是否可能用,如果可用最后設(shè)置mLaunchButton的監(jiān)聽(tīng)事件。
    • 安裝結(jié)果code為PackageManager. INSTALL_FAILED_INSUFFICIENT_STORAGE:
      表示由于設(shè)備沒(méi)有足夠的存儲(chǔ)空間來(lái)安裝該應(yīng)用程序
    • 安裝結(jié)果code為其他值:
      如果不是上面兩個(gè)code值則表示安裝失敗,通過(guò)調(diào)用getExplanationFromErrorCode(int) 方法來(lái)獲取失敗的原因文案提示并更新UI
  • 第四步:根據(jù)上面的安裝結(jié)果,更新UI。

這里面涉及到兩個(gè)重要方法:

  • 1、getExplanationFromErrorCode(int)方法
  • 2、getPackageManager().getLaunchIntentForPackage(mAppInfo.packageName)方法

下面我們就簡(jiǎn)單介紹這兩個(gè)方法:

(1.3)、 getExplanationFromErrorCode(int)方法

代碼在InstallAppProgress.java 76行

    private int getExplanationFromErrorCode(int errCode) {
        Log.d(TAG, "Installation error code: " + errCode);
        switch (errCode) {
            case PackageManager.INSTALL_FAILED_INVALID_APK:
                return R.string.install_failed_invalid_apk;
            case PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES:
                return R.string.install_failed_inconsistent_certificates;
            case PackageManager.INSTALL_FAILED_OLDER_SDK:
                return R.string.install_failed_older_sdk;
            case PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE:
                return R.string.install_failed_cpu_abi_incompatible;
            default:
                return -1;
        }
    }

通過(guò)上面代碼我知道getExplanationFromErrorCode(int) 主要是根據(jù)不同的code返回不同的String用以提醒用戶(hù)。那我們就把上面四個(gè)case依次說(shuō)下

  • PackageManager.INSTALL_FAILED_INVALID_APK:他表示無(wú)效的APK文件,一般是APK文件有問(wèn)題
  • PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES:表示簽名問(wèn)題,一般是簽名沖突
  • PackageManager.INSTALL_FAILED_OLDER_SDK:表示SDK版本和APK的要求的版本沖突
  • PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE:由于native的代碼與設(shè)備上的CPU_ABI不兼容

這里先不講解PackageManager的getLaunchIntentForPackage,大家現(xiàn)在這里打個(gè)斷點(diǎn),一會(huì)回來(lái)看這里

(2)、PackageManager的installExistingPackage(String)方法簡(jiǎn)介

代碼在PackageManager.java 3755行

    /**
     * If there is already an application with the given package name installed
     * on the system for other users, also install it for the calling user.
     * @hide
     */
    // @SystemApi
    public abstract int installExistingPackage(String packageName)
            throws NameNotFoundException;

咦,它也是一個(gè)抽象方法啊?先看下注釋?zhuān)?/p>

如果系統(tǒng)上已經(jīng)有其他用戶(hù)安裝了相同包名的應(yīng)用程序,則讓用戶(hù)繼續(xù)安裝。

同上,這里先不講解installExistingPackage(String)方法,我們一會(huì)詳細(xì)講解。

(3)、PackageManager的installPackageWithVerificationAndEncryption(PackageInstallObserver , int , String ,VerificationParams , ContainerEncryptionParams )方法類(lèi)簡(jiǎn)介

代碼在PackageManager.java 3660行

    /**
     * Similar to
     * {@link #installPackage(Uri, IPackageInstallObserver, int, String)} but
     * with an extra verification file provided.
     *
     * @param packageURI The location of the package file to install. This can
     *            be a 'file:' or a 'content:' URI.
     * @param observer An observer callback to get notified when the package installation is
     * complete. {@link PackageInstallObserver#packageInstalled(String, Bundle, int)} will be
     * called when that happens. This parameter must not be null.
     * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
     *            {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}.
     * @param installerPackageName Optional package name of the application that
     *            is performing the installation. This identifies which market
     *            the package came from.
     * @param verificationURI The location of the supplementary verification
     *            file. This can be a 'file:' or a 'content:' URI. May be
     *            {@code null}.
     * @param manifestDigest an object that holds the digest of the package
     *            which can be used to verify ownership. May be {@code null}.
     * @param encryptionParams if the package to be installed is encrypted,
     *            these parameters describing the encryption and authentication
     *            used. May be {@code null}.
     * @hide
     */
    public abstract void installPackageWithVerification(Uri packageURI,
            PackageInstallObserver observer, int flags, String installerPackageName,
            Uri verificationURI, ManifestDigest manifestDigest,
            ContainerEncryptionParams encryptionParams);

既然它也是一個(gè)抽象方法,那我們先來(lái)看下注釋?zhuān)?/p>

和installPackage(Uri, IPackageInstallObserver, int, String)有點(diǎn)類(lèi)似,但是多了一個(gè)額外的驗(yàn)證文件。

這里先簡(jiǎn)單的說(shuō)下我們上面涉及到監(jiān)聽(tīng)器PackageInstallObserver,因?yàn)镻ackageInstallObserver還是蠻重要的,

(三)、PackageInstallObserver簡(jiǎn)介

1、PackageInstallObserver

PackageInstallObserver.java源碼地址
代碼如下:

public class PackageInstallObserver {
    private final IPackageInstallObserver2.Stub mBinder = new IPackageInstallObserver2.Stub() {
        @Override
        public void onUserActionRequired(Intent intent) {
            PackageInstallObserver.this.onUserActionRequired(intent);
        }

        @Override
        public void onPackageInstalled(String basePackageName, int returnCode,
                String msg, Bundle extras) {
            PackageInstallObserver.this.onPackageInstalled(basePackageName, returnCode, msg,
                    extras);
        }
    };

    /** {@hide} */
    public IPackageInstallObserver2 getBinder() {
        return mBinder;
    }

    public void onUserActionRequired(Intent intent) {
    }

    /**
     * This method will be called to report the result of the package
     * installation attempt.
     *
     * @param basePackageName Name of the package whose installation was
     *            attempted
     * @param extras If non-null, this Bundle contains extras providing
     *            additional information about an install failure. See
     *            {@link android.content.pm.PackageManager} for documentation
     *            about which extras apply to various failures; in particular
     *            the strings named EXTRA_FAILURE_*.
     * @param returnCode The numeric success or failure code indicating the
     *            basic outcome
     * @hide
     */
    public void onPackageInstalled(String basePackageName, int returnCode, String msg,
            Bundle extras) {
    }
}

我們看到PackageInstallObserver的getBinder()方法返回的是本地成員變量mBinder,而mBinder又是IPackageInstallObserver2.Stub。所以我們可以說(shuō)mBinder也是一個(gè)AIDL的通信的服務(wù)端。

我們看來(lái)下onPackageInstalled(String,int,String,Bundle)方法的注釋?zhuān)?/p>

調(diào)用這個(gè)方法來(lái)獲取程序安裝的結(jié)果

  • 入?yún)?basePackageName:表示安裝包的包名
  • 入?yún)?extras,可能為空,如果非空,則在里面包含導(dǎo)致安裝失敗的附加信息??梢詤⒖糰ndroid.content.pm.PackageManage類(lèi)中以EXTRA_FAILURE_開(kāi)頭的字段
  • 入?yún)?returnCode:表示安裝成功或者失敗的狀態(tài)碼

這時(shí)候就要看一下IPackageInstallObserver2.aidl

2、IPackageInstallObserver2.aidl簡(jiǎn)介

IPackageInstallObserver2.aidl源碼地址

/**
 * API for installation callbacks from the Package Manager.  In certain result cases
 * additional information will be provided.
 * @hide
 */
oneway interface IPackageInstallObserver2 {
    void onUserActionRequired(in Intent intent);

    /**
     * The install operation has completed.  {@code returnCode} holds a numeric code
     * indicating success or failure.  In certain cases the {@code extras} Bundle will
     * contain additional details:
     *
     * <p><table>
     * <tr>
     *   <td>INSTALL_FAILED_DUPLICATE_PERMISSION</td>
     *   <td>Two strings are provided in the extras bundle: EXTRA_EXISTING_PERMISSION
     *       is the name of the permission that the app is attempting to define, and
     *       EXTRA_EXISTING_PACKAGE is the package name of the app which has already
     *       defined the permission.</td>
     * </tr>
     * </table>
     */
    void onPackageInstalled(String basePackageName, int returnCode, String msg, in Bundle extras);
}
(1)、"oneway"關(guān)鍵字

首先說(shuō)下AIDL的關(guān)鍵字oneway"",AIDL可以用關(guān)鍵字"oneway"來(lái)表明遠(yuǎn)程調(diào)用的行為屬性,使用了該關(guān)鍵字,那么遠(yuǎn)程調(diào)用將緊緊是調(diào)用所有的數(shù)據(jù)傳輸過(guò)來(lái)并立即返回,而不會(huì)等待結(jié)果的返回,也是說(shuō)不會(huì)阻塞遠(yuǎn)程線(xiàn)程的運(yùn)行。AIDL接口將最終獲得一個(gè)從Binder線(xiàn)程池中產(chǎn)生的調(diào)用(和普通的遠(yuǎn)程調(diào)用類(lèi)似)。如果關(guān)鍵字oneway在本地調(diào)用中被使用,將不會(huì)對(duì)函數(shù)調(diào)用有任何影響

(2)、理解"注釋"

為了更好的理解設(shè)計(jì)者最初的設(shè)想,我們來(lái)看下"類(lèi)"的注釋

包管理其用于安裝的的回調(diào)API。在某些情況下,它可以提供一些必要的信息。

那我們來(lái)看下onPackageInstalled的注釋

安裝操作完成后,會(huì)包含一個(gè)code,由這個(gè)code標(biāo)識(shí)成功或者失敗。在特定的情形下它也會(huì)包含一些附加信息:
比如INSTALL_FAILED_DUPLICATE_PERMISSION這個(gè)安裝失?。?br> 它的extras 的Bundle 里面可能會(huì)存在EXTRA_EXISTING_PERMISSION這個(gè)字符串,表示定義app向定義權(quán)限的內(nèi)容,和EXTRA_EXISTING_PACKAGE這個(gè)字符串,表示應(yīng)定義了權(quán)限的應(yīng)用程序包名稱(chēng)。

(3)、為什么要設(shè)計(jì)成AIDL

因?yàn)槟阍贗nstallAppProgress是一個(gè)單獨(dú)的進(jìn)程,而PackageManagerService也是一個(gè)單獨(dú)的進(jìn)程,假設(shè)InstallAppProgress所在的進(jìn)程名稱(chēng)為"InstallAppProgress",你想在"InstallAppProgress"監(jiān)聽(tīng)"PackageManagerService"進(jìn)程的結(jié)果,這里面涉及到兩個(gè)進(jìn)程的調(diào)用,是屬于跨進(jìn)程調(diào)用,所以需要使用Binder來(lái)進(jìn)行進(jìn)程間通信。

(三)、總結(jié)

PackageInstallObserver類(lèi)內(nèi)部含有一個(gè)AIDL的Binder跨進(jìn)程通信,當(dāng)在PackageManagerService中安裝完成,會(huì)調(diào)用IPackageInstallObserver2.Stub的onPackageInstalled方法,然后在IPackageInstallObserver2.Stub 的onPackageInstalled方法里面調(diào)用PackageInstallObserver的onPackageInstalled。這樣就通知到了InstallAppProgress的observer對(duì)象了

九、InstallAppProgress中涉及到PackageManager的三個(gè)方法

InstallAppProgress.java 中的

大體流程如下,如果如果已經(jīng)存在安裝包了,則更新(調(diào)用PackageManager的installExistingPackage(String)方法),如果沒(méi)有新安裝,則進(jìn)行安裝(PackageManager的installPackageWithVerificationAndEncryption方法),最后無(wú)論是更新還是新安裝成功都會(huì)調(diào)用PackageManager的getLaunchIntentForPackage方法

因?yàn)樵蹅冎饕菚?huì)講解新安裝,所有后面主要是跟蹤PackageManager的installPackageWithVerificationAndEncryption方法,大家可以自己去跟蹤PackageManager的installExistingPackage(String)方法,其實(shí)原理大差不差,很多方法都是公用的。

上一篇文章 APK安裝流程詳解10——PackageParser解析APK(下)
下一篇文章 APK安裝流程詳解12——PackageManagerService中的新安裝流程上(拷貝)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容