android PKMS-3 session處理流程詳解

1 概述

關(guān)于session的處理流程,我們要先了解session是什么碎绎,它用來(lái)做什么

PackageInstaller.Session 是 Android 系統(tǒng)提供的一個(gè)類(lèi),用于在運(yùn)行時(shí)安裝應(yīng)用程序包(APK)文件。通過(guò) PackageInstaller.Session,開(kāi)發(fā)者可以在應(yīng)用程序中實(shí)現(xiàn)動(dòng)態(tài)安裝應(yīng)用程序捏膨,而不需要依賴(lài)傳統(tǒng)的安裝方式(如通過(guò)應(yīng)用商店或命令行)。

使用 PackageInstaller 和 PackageInstaller.Session 類(lèi)食侮,開(kāi)發(fā)者可以進(jìn)行應(yīng)用程序的分階段安裝号涯,管理安裝過(guò)程中的權(quán)限請(qǐng)求、安裝進(jìn)度等操作锯七。這樣的安裝方式可以提供更好的用戶(hù)體驗(yàn)链快,同時(shí)也增加了安全性和靈活性。

通常情況下眉尸,PackageInstaller.Session 會(huì)被用于應(yīng)用程序更新域蜗、應(yīng)用內(nèi)動(dòng)態(tài)模塊安裝等場(chǎng)景中巨双。通過(guò)該類(lèi),開(kāi)發(fā)者可以控制應(yīng)用程序的安裝過(guò)程地消,并且可以處理各種安裝過(guò)程中可能出現(xiàn)的情況炉峰。

2 代碼流程

2.1 session的第一次處理
        public void commit(@NonNull IntentSender statusReceiver) {
            try {
                //這里調(diào)用IPackageInstallerSession的commit方法
                mSession.commit(statusReceiver, false);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
IPackageInstallerSession.aidl
interface IPackageInstallerSession 

由此可以知道當(dāng)次我們進(jìn)行跨進(jìn)程通信,systemserver進(jìn)程

public class PackageInstallerSession extends IPackageInstallerSession.Stub {

    public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
        /**
         * 參數(shù) statusReceiver 用于組件通信
         * 攜帶的數(shù)據(jù) 安裝包名稱(chēng)  安裝包id
         * forTransfer false
         */
        if (hasParentSessionId()) {
            /**
             * case 是否存在父session
             * 
             * 拋出異常脉执,當(dāng)次session提交失效
             */
            throw new IllegalStateException(
                    "Session " + sessionId + " is a child of multi-package session "
                            + getParentSessionId() +  " and may not be committed directly.");
        }
        //檢查當(dāng)次會(huì)話,無(wú)問(wèn)題的話 確定當(dāng)次session
        if (!markAsSealed(statusReceiver, forTransfer)) {
            return;
        }
        if (isMultiPackage()) {
            /**
             * case 多安裝包情況 
             * 
             * 過(guò)程 遍歷每一個(gè)安裝包 并且標(biāo)志 session確定
             * 
             *      只要其中一個(gè)安裝包session不確定戒劫,都會(huì)導(dǎo)致安裝失敗
             */
            synchronized (mLock) {
                boolean sealFailed = false;
                for (int i = mChildSessions.size() - 1; i >= 0; --i) {
                    // seal all children, regardless if any of them fail; we'll throw/return
                    // as appropriate once all children have been processed
                    if (!mChildSessions.valueAt(i).markAsSealed(null, forTransfer)) {
                        sealFailed = true;
                    }
                }
                if (sealFailed) {
                    return;
                }
            }
        }
        //當(dāng)會(huì)話確定完成 這里進(jìn)行會(huì)話派發(fā)
        dispatchSessionSealed();
    }

}

總結(jié): 系統(tǒng)應(yīng)用進(jìn)程封裝session參數(shù)傳入到system_server進(jìn)程后半夷,創(chuàng)建PackageinstallSession,開(kāi)啟異步任務(wù)向讓session保存安裝apk的文件數(shù)據(jù)迅细,接下來(lái)執(zhí)行session.commit跨進(jìn)程來(lái)到PMS進(jìn)行交互
當(dāng)前主要進(jìn)行session確定操作巫橄,檢查當(dāng)前系統(tǒng)frp是否開(kāi)啟且禁用安裝功能,sessionid是否存在轉(zhuǎn)移
檢查完畢 封裝會(huì)話茵典,派發(fā)出去

  private boolean markAsSealed(@Nullable IntentSender statusReceiver, boolean forTransfer) {
        /**
         * 參數(shù) statusReceiver 不為null  forTransfer false
         */
        Preconditions.checkState(statusReceiver != null || hasParentSessionId(),
                "statusReceiver can't be null for the root session");
        assertCallerIsOwnerOrRoot();

        synchronized (mLock) {
            assertPreparedAndNotDestroyedLocked("commit of session " + sessionId);
            assertNoWriteFileTransfersOpenLocked();
            //獲取SECURE_FRP_MODE的值 FRP 是一種安全功能湘换,旨在保護(hù)設(shè)備免受未經(jīng)授權(quán)的訪問(wèn)和數(shù)據(jù)泄露。
            final boolean isSecureFrpEnabled =
                    (Secure.getInt(mContext.getContentResolver(), Secure.SECURE_FRP_MODE, 0) == 1);
            if (isSecureFrpEnabled
                    && !isSecureFrpInstallAllowed(mContext, Binder.getCallingUid())) {
                /**
                 * case frp開(kāi)啟且 frp禁止安裝操作
                 * 
                 * 過(guò)程  拋出異常统阿,當(dāng)次安裝失敗
                 */
                throw new SecurityException("Can't install packages while in secure FRP");
            }

            if (forTransfer) {
                /**
                 * case 檢查安裝會(huì)話是否被修改
                 * 
                 * 過(guò)程 修改  檢查倆次安裝是否相同彩倚,相同 拋出異常
                 * 
                 *      未修改  不相同拋出異常
                 */
                mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
                if (mInstallerUid == mOriginalInstallerUid) {
                    throw new IllegalArgumentException("Session has not been transferred");
                }
            } else {
                if (mInstallerUid != mOriginalInstallerUid) {
                    throw new IllegalArgumentException("Session has been transferred");
                }
            }

            setRemoteStatusReceiver(statusReceiver);

            // After updating the observer, we can skip re-sealing.
            if (mSealed) {
                return true;
            }

            try {
                //設(shè)置mseal標(biāo)志位定義當(dāng)前會(huì)話已被seal確定
                sealLocked();
            } catch (PackageManagerException e) {
                return false;
            }
        }

        return true;
    }
2.2 session的第二次處理
    private void dispatchSessionSealed() {
        //發(fā)送消息MSG_ON_SESSION_SEALED
        mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
    }

可以看到是通過(guò)handler消息機(jī)制來(lái)驅(qū)動(dòng)的,在android系統(tǒng)中這種驅(qū)動(dòng)方式很常見(jiàn)

    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_ON_SESSION_SEALED:
                    handleSessionSealed();
                    break;

上面session剛確認(rèn)好扶平,這里執(zhí)行session確認(rèn)完成流程

    private void handleSessionSealed() {
        assertSealed("dispatchSessionSealed");
        // Persist the fact that we've sealed ourselves to prevent
        // mutations of any hard links we create.
        /**
         * 這里是一個(gè)回調(diào)操作帆离,了解這個(gè)回調(diào)的機(jī)制流程
         * mCallback 是 PackageInstallerSession的內(nèi)部類(lèi) InternalCallback
         * InternalCallback 執(zhí)行該方法時(shí)
         * 又會(huì)引入一個(gè)新變量mCallbacks 他實(shí)際是一個(gè)handler子類(lèi)
         *  private class Callbacks extends Handler
         * 
         * 通過(guò)handler來(lái)驅(qū)動(dòng)消息,最后你會(huì)看到IPackageInstallerCallback來(lái)處理回調(diào)
         * 這個(gè)一看就知道是aidl回調(diào) 那么就是會(huì)通知相關(guān)進(jìn)程當(dāng)前安裝的一個(gè)狀態(tài)
         * 可以先看我這個(gè)了解下 文章最后我會(huì)舉一個(gè)例子
         */
        mCallback.onSessionSealedBlocking(this);
        //接下來(lái)就是apk文件流的驗(yàn)證和提交
        dispatchStreamValidateAndCommit();
    }
    private void dispatchStreamValidateAndCommit() {
        mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
    }
                case MSG_STREAM_VALIDATE_AND_COMMIT:
                    handleStreamValidateAndCommit();
                    break;
    private void handleStreamValidateAndCommit() {
        try {
            // This will track whether the session and any children were validated and are ready to
            // progress to the next phase of install
            //allSessionsReady 作為標(biāo)志位 默認(rèn)當(dāng)前session都已經(jīng)準(zhǔn)備就緒
            boolean allSessionsReady = true;
            //這里如果是多包安裝结澄,需要對(duì)每個(gè)包的數(shù)據(jù)都進(jìn)行驗(yàn)證
            /**
             * case 多安裝包情況
             * 過(guò)程 每個(gè)子安裝包都要進(jìn)行數(shù)據(jù)驗(yàn)證哥谷,最后確認(rèn)Session是否準(zhǔn)備就緒
             */
            for (PackageInstallerSession child : getChildSessions()) {
                allSessionsReady &= child.streamValidateAndCommit();
            }
            /**
             * case 普通安裝單包情況
             * 
             * 過(guò)程 直接進(jìn)行streamValidateAndCommit 數(shù)據(jù)包驗(yàn)證操作
             *      驗(yàn)證成功會(huì)通過(guò)handler驅(qū)動(dòng)下一次操作
             */
            if (allSessionsReady && streamValidateAndCommit()) {
                mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
            }
        } catch (PackageManagerException e) {
            //失敗了也要通知相關(guān)進(jìn)程或者服務(wù)
            destroy();
            String msg = ExceptionUtils.getCompleteMessage(e);
            dispatchSessionFinished(e.error, msg, null);
            maybeFinishChildSessions(e.error, msg);
        }
    }

總結(jié) 這里主要針對(duì)安裝apk的數(shù)據(jù)進(jìn)行一個(gè)驗(yàn)證,多安裝包麻献,分批檢查们妥,單安裝包,直接檢查

    private void handleStreamValidateAndCommit() {
        try {
            // This will track whether the session and any children were validated and are ready to
            // progress to the next phase of install
            //allSessionsReady 作為標(biāo)志位 默認(rèn)當(dāng)前session都已經(jīng)準(zhǔn)備就緒
            boolean allSessionsReady = true;
            //這里如果是多包安裝勉吻,需要對(duì)每個(gè)包的數(shù)據(jù)都進(jìn)行驗(yàn)證
            /**
             * case 多安裝包情況
             * 過(guò)程 每個(gè)子安裝包都要進(jìn)行數(shù)據(jù)驗(yàn)證监婶,最后確認(rèn)Session是否準(zhǔn)備就緒
             */
            for (PackageInstallerSession child : getChildSessions()) {
                allSessionsReady &= child.streamValidateAndCommit();
            }
            /**
             * case 普通安裝單包情況
             * 
             * 過(guò)程 直接進(jìn)行streamValidateAndCommit 數(shù)據(jù)包驗(yàn)證操作
             *      驗(yàn)證成功會(huì)通過(guò)handler驅(qū)動(dòng)下一次操作
             */
            if (allSessionsReady && streamValidateAndCommit()) {
                mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
            }
        } catch (PackageManagerException e) {
            //失敗了也要通知相關(guān)進(jìn)程或者服務(wù)
            destroy();
            String msg = ExceptionUtils.getCompleteMessage(e);
            dispatchSessionFinished(e.error, msg, null);
            maybeFinishChildSessions(e.error, msg);
        }
    }

總結(jié): session的派發(fā)中,第一次進(jìn)行了session的條件判斷確認(rèn)餐曼,第二次進(jìn)行了session內(nèi)安裝包的數(shù)據(jù)檢查驗(yàn)證压储,接下來(lái)該來(lái)到session的第三次處理流程

                case MSG_INSTALL:
                    handleInstall();
                    break;
    private void handleInstall() {
        if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
            //跟蹤設(shè)備上應(yīng)用程序的安裝情況
            DevicePolicyEventLogger
                    .createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
                    .setAdmin(getInstallSource().installerPackageName)
                    .write();
        }

        /**
         * Stops the installation of the whole session set if one session needs user action
         * in its belong session set. When the user answers the yes,
         * {@link #setPermissionsResult(boolean)} is called and then {@link #MSG_INSTALL} is
         * handled to come back here to check again.
         */
        if (sendPendingUserActionIntentIfNeeded()) {
            return;
        }
        //根據(jù)參數(shù)params的isStaged檢查當(dāng)前是否分階段驗(yàn)證
        /** 
         * params 是packageInstallerSession構(gòu)建的時(shí)候傳入的 PackageInstaller.SessionParams
         * 封裝著安裝包的相關(guān)信息
         * case 是否分階段
         * 過(guò)程 分階段使用mStagedSession
         *      不分階段使用verify
         * 這里分析不分階段
        */
        if (params.isStaged) {
            mStagedSession.verifySession();
        } else {
            //普通驗(yàn)證流程
            verify();
        }
    }
    private void verify() {
        try {
            /**
             * 可以看到verify主要的工作就是解析安裝包的文件數(shù)據(jù)
             * case 多安裝包
             * 過(guò)程  遍歷子安裝包解析每一個(gè)子安裝包的文件
             * 
             * case 單安裝包
             * 
             * 直接解析
             */
            List<PackageInstallerSession> children = getChildSessions();
            if (isMultiPackage()) {
                for (PackageInstallerSession child : children) {
                    child.prepareInheritedFiles();
                    child.parseApkAndExtractNativeLibraries();
                }
            } else {
                prepareInheritedFiles();
                parseApkAndExtractNativeLibraries();
            }
            //解析完成執(zhí)行
            verifyNonStaged();
        } catch (PackageManagerException e) {
            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
            final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
            setSessionFailed(e.error, errorMsg);
            onSessionVerificationFailure(e.error, errorMsg);
        }
    }

解析涉及代碼不多,我們也看下解析相關(guān)

   private void parseApkAndExtractNativeLibraries() throws PackageManagerException {
        synchronized (mLock) {
            /**
             * 在解析安裝包文件前會(huì)進(jìn)行一些case判斷
             * case 當(dāng)前解析的目錄/文件是否已經(jīng)被使用了
             * 
             * case mDestroyed = true
             * 
             * case mSealed = false
             * 
             * 這三種情況都會(huì)拋出異常源譬,提示安裝失敗原因
             */
            if (mStageDirInUse) {
                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                        "Session files in use");
            }
            if (mDestroyed) {
                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                        "Session destroyed");
            }
            if (!mSealed) {
                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                        "Session not sealed");
            }
            Objects.requireNonNull(mPackageName);
            Objects.requireNonNull(mSigningDetails);
            Objects.requireNonNull(mResolvedBaseFile);
            final PackageLite result;
            /**
             * case 判斷當(dāng)次解析的session是否為apex
             * 
             * 過(guò)程 這里我們分析apk安裝 所以執(zhí)行g(shù)etOrParsePackageLiteLocked 解析拿到PackageLite
             * 
             * 這個(gè)PackageLite是包含應(yīng)用程序相關(guān)信息集惋,當(dāng)然不是完整的,后續(xù)會(huì)看到他的具體作用
             * 
             * 最后解析apk包含的lib庫(kù)文件
             */
            if (!isApexSession()) {
                // For mode inherit existing, it would link/copy existing files to stage dir in
                // prepareInheritedFiles(). Therefore, we need to parse the complete package in
                // stage dir here.
                // Besides, PackageLite may be null for staged sessions that don't complete
                // pre-reboot verification.
                result = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
            } else {
                result = getOrParsePackageLiteLocked(mResolvedBaseFile, /* flags */ 0);
            }
            if (result != null) {
                mPackageLite = result;
                if (!isApexSession()) {
                    synchronized (mProgressLock) {
                        mInternalProgress = 0.5f;
                        computeProgressLocked(true);
                    }
                    extractNativeLibraries(
                            mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
                }
            }
        }
    }

總結(jié):主要就是解析apk部分信息拿到ePackageLite踩娘,解析native lib庫(kù)文件
解析完成開(kāi)始下一步流程

2.3 session的第三次處理
    private void verifyNonStaged()
            throws PackageManagerException {
        synchronized (mLock) {
            markStageDirInUseLocked();
        }
        //這里主要進(jìn)行session 驗(yàn)證 驗(yàn)證通過(guò)執(zhí)行內(nèi)部回調(diào)函數(shù)
        /**
         * 這里通過(guò)會(huì)話提供器獲取會(huì)話驗(yàn)證器刮刑,來(lái)驗(yàn)證當(dāng)前安裝會(huì)話
         * 
         * 結(jié)果 error 報(bào)錯(cuò)  msg  異常消息
         * 
         * 驗(yàn)證安裝會(huì)話后喉祭,會(huì)通過(guò)匿名函數(shù)回調(diào)的方式通過(guò)handler來(lái)給到主線程處理callback任務(wù)
         * 
         * case 安裝成功
         * 
         * 執(zhí)行會(huì)話驗(yàn)證完成回調(diào)
         * 
         * case 安裝失敗
         * 執(zhí)行會(huì)話驗(yàn)證失敗的回調(diào)
         */
        mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
            mHandler.post(() -> {
                if (dispatchPendingAbandonCallback()) {
                    // No need to continue if abandoned
                    return;
                }
                if (error == INSTALL_SUCCEEDED) {
                    //這里執(zhí)行該方法
                    onVerificationComplete();
                } else {
                    onSessionVerificationFailure(error, msg);
                }
            });
        });
    }

總結(jié) 主要是對(duì)安裝會(huì)話進(jìn)行驗(yàn)證相關(guān)操作,sessionVerifier.verify的具體處理流程的話雷绢,后續(xù)單獨(dú)出文章詳細(xì)分析泛烙,先了解宏觀的結(jié)構(gòu)

    private void onVerificationComplete() {
        //Android應(yīng)用安裝過(guò)程中,引入了"staged"(暫存)階段作為安裝的一部分翘紊。在這個(gè)階段蔽氨,
        //安裝系統(tǒng)會(huì)將安裝操作拆分成多個(gè)步驟來(lái)執(zhí)行,而不是一次性完成整個(gè)安裝過(guò)程帆疟。
        //這種方式有助于提高安裝的效率和穩(wěn)定性鹉究,尤其是對(duì)于大型應(yīng)用或多模塊應(yīng)用的安裝
        /**
         * case 檢查當(dāng)次安裝是否需要分階段
         * 過(guò)程 這里執(zhí)行普通安裝操作
         */
        if (isStaged()) {
            //分階段安裝操作
            mStagingManager.commitSession(mStagedSession);
            sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null);
            return;
        }
        //普通安裝操作
        install();
    }

在查看安裝操作時(shí),我們需要先了解踪宠,這里的一個(gè)用到的一個(gè)java語(yǔ)法CompletableFuture 它也是一個(gè)異步任務(wù)的對(duì)象
由java8 引入的一個(gè)概念自赔,我們可以管理異步任務(wù)的完成狀態(tài),并且可以對(duì)異步任務(wù)的處理結(jié)果進(jìn)行相關(guān)處理

如何創(chuàng)建completableFuture對(duì)象
CompletableFuture.supplyAsync方法柳琢,或者CompletableFuture.runAsync方法

我們也可以組成多個(gè)completableFuture對(duì)象绍妨,通過(guò)使用thenApply,thenAccept柬脸,thenCombine方法來(lái)管理這些異步任務(wù)的執(zhí)行順序

eg

java
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class CompletableFutureDemo {

    public static void main(String[] args) {
        // 創(chuàng)建一個(gè)CompletableFuture來(lái)執(zhí)行異步任務(wù)
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                // 模擬耗時(shí)操作
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Hello, CompletableFuture!";
        });

        // 定義在CompletableFuture完成后執(zhí)行的操作
        CompletableFuture<Void> resultFuture = future.thenAccept(result -> {
            System.out.println("Result received: " + result);
        });

        // 等待異步任務(wù)完成
        resultFuture.join();

        System.out.println("CompletableFuture demo finished.");
    }
}

如何處理completableFuture拋出的異常
使用exceptionally方法
eg

import java.util.concurrent.CompletableFuture;

public class CompletableFutureExceptionDemo {

    public static void main(String[] args) {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            // 模擬一個(gè)會(huì)拋出異常的異步任務(wù)
            if (Math.random() < 0.5) {
                throw new RuntimeException("Simulated exception");
            }
            return 42;
        });

        // 使用exceptionally()處理異常
        CompletableFuture<Integer> resultFuture = future.exceptionally(ex -> {
            System.out.println("Exception occurred: " + ex.getMessage());
            return -1; // 返回一個(gè)備用結(jié)果
        });

        resultFuture.thenAccept(result -> {
            System.out.println("Result: " + result);
        });

        try {
            resultFuture.get(); // 等待異步任務(wù)完成
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

使用allof方法可以等待所有任務(wù)執(zhí)行完成
eg

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureAllOfDemo {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Result from future1");
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Result from future2");
        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Result from future3");

        CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2, future3);

        // 等待所有CompletableFuture完成
        allFutures.get();

        System.out.println("All CompletableFuture completed.");
        System.out.println("Result 1: " + future1.get());
        System.out.println("Result 2: " + future2.get());
        System.out.println("Result 3: " + future3.get());
    }

接著我們接著看install函數(shù)

    private CompletableFuture<Void> install() {
        //獲取異步任務(wù)集合里面存放著安裝的結(jié)果 
        //從這里可以猜到installNonStaged可能是具體的安裝操作
        List<CompletableFuture<InstallResult>> futures = installNonStaged();
        //根據(jù)集合的數(shù)量創(chuàng)建對(duì)應(yīng)數(shù)組
        CompletableFuture<InstallResult>[] arr = new CompletableFuture[futures.size()];
        //這里當(dāng)安裝操作都完成后他去,獲取安裝的結(jié)果和異常,執(zhí)行異步任務(wù)
        return CompletableFuture.allOf(futures.toArray(arr)).whenComplete((r, t) -> {
            if (t == null) {
                //沒(méi)有異常的情況肖粮,處理異步任務(wù)完成的結(jié)果孤页,派發(fā)通知當(dāng)前安裝會(huì)話完成
                setSessionApplied();
                for (CompletableFuture<InstallResult> f : futures) {
                    InstallResult result = f.join();
                    result.session.dispatchSessionFinished(
                            INSTALL_SUCCEEDED, "Session installed", result.extras);
                }
            } else {
                //安裝存在異常,設(shè)置會(huì)話失敗涩馆,派發(fā)通知當(dāng)前安裝會(huì)話失敗
                PackageManagerException e = (PackageManagerException) t.getCause();
                setSessionFailed(e.error,
                        PackageManager.installStatusToString(e.error, e.getMessage()));
                dispatchSessionFinished(e.error, e.getMessage(), null);
                maybeFinishChildSessions(e.error, e.getMessage());
            }
        });
    }

installNonStaged函數(shù)返回異步任務(wù)集合
現(xiàn)看installNonStaged函數(shù)

    private List<CompletableFuture<InstallResult>> installNonStaged() {
        try {
            //構(gòu)建異步任務(wù)集合
            List<CompletableFuture<InstallResult>> futures = new ArrayList<>();
            //首先構(gòu)建異步任務(wù)加入集合
            CompletableFuture<InstallResult> future = new CompletableFuture<>();
            futures.add(future);
            //將異步任務(wù)關(guān)聯(lián)到InstallParams中
            final InstallParams installingSession = makeInstallParams(future);
            if (isMultiPackage()) {
                //case1 多apk情況
                //獲取子會(huì)話
                final List<PackageInstallerSession> childSessions = getChildSessions();
                //創(chuàng)建子會(huì)話集合
                List<InstallParams> installingChildSessions = new ArrayList<>(childSessions.size());
                for (int i = 0; i < childSessions.size(); ++i) {
                    //遍歷子會(huì)話行施,構(gòu)建異步任務(wù)關(guān)聯(lián)子會(huì)話,
                    final PackageInstallerSession session = childSessions.get(i);
                    future = new CompletableFuture<>();
                    futures.add(future);
                    final InstallParams installingChildSession = session.makeInstallParams(future);
                    if (installingChildSession != null) {
                        installingChildSessions.add(installingChildSession);
                    }
                }
                //這里子會(huì)話不為空
                if (!installingChildSessions.isEmpty()) {
                    //執(zhí)行安裝操作
                    installingSession.installStage(installingChildSessions);
                }
            } else if (installingSession != null) {
                //對(duì)于普通安裝 正常執(zhí)行安裝操作
                installingSession.installStage();
            }

            return futures;
        } catch (PackageManagerException e) {
            List<CompletableFuture<InstallResult>> futures = new ArrayList<>();
            futures.add(CompletableFuture.failedFuture(e));
            return futures;
        }
    }

總結(jié): 在這里可以看到根據(jù)多包安裝還是單包安裝魂那,我們會(huì)構(gòu)建CompletableFuture對(duì)象蛾号,通過(guò)makeInstallParams函數(shù)將future關(guān)聯(lián)到InstallParams中,當(dāng)InstallParams準(zhǔn)備好后涯雅,會(huì)調(diào)用installStage函數(shù)執(zhí)行安裝操作并且將InstallParams傳遞進(jìn)去

接著回到install函數(shù)鲜结,當(dāng)所有異步任務(wù)完成并且沒(méi)有異常的話,設(shè)置安裝session被使用活逆,主線程等待異步任務(wù)完成精刷,異步任務(wù)完成后執(zhí)行dispatchSessionFinished派發(fā)出去通知已安裝

到這里我們跟蹤了installSession的整個(gè)處理流程

接下來(lái)我們會(huì)詳細(xì)去看安裝的流程細(xì)節(jié),還有dispatchSessionFinished的派發(fā)最后通知了誰(shuí)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蔗候,一起剝皮案震驚了整個(gè)濱河市怒允,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锈遥,老刑警劉巖纫事,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勘畔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡丽惶,警方通過(guò)查閱死者的電腦和手機(jī)炫七,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)钾唬,“玉大人万哪,你說(shuō)我怎么就攤上這事÷崭眩” “怎么了壤圃?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)琅轧。 經(jīng)常有香客問(wèn)我,道長(zhǎng)踊挠,這世上最難降的妖魔是什么乍桂? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮效床,結(jié)果婚禮上睹酌,老公的妹妹穿的比我還像新娘。我一直安慰自己剩檀,他們只是感情好憋沿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著沪猴,像睡著了一般辐啄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上运嗜,一...
    開(kāi)封第一講書(shū)人閱讀 51,182評(píng)論 1 299
  • 那天壶辜,我揣著相機(jī)與錄音,去河邊找鬼担租。 笑死砸民,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的奋救。 我是一名探鬼主播岭参,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼尝艘!你這毒婦竟也來(lái)了演侯?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤利耍,失蹤者是張志新(化名)和其女友劉穎蚌本,沒(méi)想到半個(gè)月后盔粹,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡程癌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年舷嗡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嵌莉。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡进萄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出锐峭,到底是詐尸還是另有隱情中鼠,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布沿癞,位于F島的核電站援雇,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏椎扬。R本人自食惡果不足惜惫搏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蚕涤。 院中可真熱鬧筐赔,春花似錦、人聲如沸揖铜。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)天吓。三九已至贿肩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間失仁,已是汗流浹背尸曼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留萄焦,地道東北人控轿。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像拂封,于是被迫代替她去往敵國(guó)和親茬射。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容