Activity啓動流程和源碼解析

↓雙擊 Markdown插件,就可以開始編輯啦。

APP進程

ActivityThread的啓動

public static void main(String[] args) {
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
    }

在attach方法中主要做了以下重要的事情:

final IActivityManager mgr = ActivityManager.getService();
            try {
                mgr.attachApplication(mAppThread, startSeq);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }

attach方法把應用進程的ApplicationThread通過IActivityManager傳遞到ActivityManagerService,而IActivityManager是ActivityManagerService系統服務在APP進程的一個代理,這個是一個跨進程的調用,最終會調用到ActivityManagerService的attachApplication方法:

public final void attachApplication(IApplicationThread thread, long startSeq) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
            Binder.restoreCallingIdentity(origId);
        }
    }

attachApplication方法裏面又調用到:
在這裏插入圖片描述
IApplicationThread是APP進程ApplicationThread在系統進程的一個代理對象,bindApplication是一個系統進程到APP進程的跨進層調用,負責把一些初始化的數據回傳到APP進程,例如系統服務的各種Binder代理對象,bindApplication最後會調用到ApplicationThread的bindApplication方法,這裏面主要做了三件事:

  • 一個是把系統服務的Binder對象緩存到ServiceManager中
  • 第二個是創建LoadedApk對象,LoadedApk是應用在內存上的一個體現,包含了應用信息,資源路徑,內部文件路徑,apk路徑,receiver信息,Service信息
  • 第三個是通過Handler類對象H來實現Application的創建,onCreate的回調

ActivityThread的啓動流程

在這裏插入圖片描述
App進程與AMS進程的通信過程如圖所示
在這裏插入圖片描述

系統進程

SystemServer啓動

/**
     * The main entry point from zygote.
     */
    public static void main(String[] args) {
        new SystemServer().run();
    }

在system_process進程啓動的時候就會初始化一些系統服務,例如ActivityManagerService:
在這裏插入圖片描述

APP進程啓動

在這裏插入圖片描述
通過LocalSocket來用命令的方式啓動Process
在這裏插入圖片描述

Activity啓動

Activity啓動的核心類

  • ActivityThread:應用進程的啓動初始化和四大組件在應用進程的調度
  • ApplicationThread:應用進程和系統進程誇進程通信的服務端
  • IActivityManager:ActivityManagerService在應用進程的binder代理對象,系統進程和應用進程進行跨進程通信的客戶端
  • SystemServer:系統進程的啓動初始化
  • ActivityManagerService:四大組件在系統進程的調度,系統進程和應用進程進行誇進程通信的服務端
  • IApplicationThread:ApplicationThread在系統進程的binder代理對象,系統進程和應用進程進行誇進程通信的客戶端
  • ServiceManager:系統服務的管理

Activity啓動過程

在這裏插入圖片描述

  • 通過Context的startActivity方法發起Activity的啓動
  • 然後進入ContextImpl中的startActivity方法,並在該方法中通過ActivityThread獲取到Instrumentation對象,並調用execStartActivity方法
  • 在execStartActivity方法中會通過IActivityManager代理對象觸發誇進程調用,把Activity的一些信息參數傳遞到系統進程ActivityManagerService中
  • 在ActivityManagerService會逐層調用,然後進入ActivityStarter類中的startActivityMayWait方法,ActivityStarter會進行一些權限檢查,並創建ActivityRecord對象把Activity信息存到該對象中去
  • 加下來會執行到ActivityStack中,顧名思義,這個是對Activity棧進行管理的操作
  • 接着繼續執行到ActivityStackSupervisor類的startSpecificActivityLocked方法,在這個方法裏面就會去判斷當前需要啓動的Activity所在的進程是否已經存在如果存在則調用realStartActivityLocked方法,否則就startProcessAsync方法優先啓動進程
  • 接下來Android8.0以後的代碼和8.0以前的代碼就有一些小差異,但是做的事情都是一樣的:通過IApplicationThread跨進程通知應用進程,並繼續在應用進程執行下一步操作
  • 應用進程在收到通知後會在Handler類H中去執行Activity的創建以及回掉各個Activity的生命週期的方法(通過狀態模式來實現Activity的生命週期的切換)
private void performLifecycleSequence(ActivityClientRecord r, IntArray path,
            ClientTransaction transaction) {
        final int size = path.size();
        for (int i = 0, state; i < size; i++) {
            state = path.get(i);
            if (DEBUG_RESOLVER) {
                Slog.d(TAG, tId(transaction) + "Transitioning activity: "
                        + getShortActivityName(r.token, mTransactionHandler)
                        + " to state: " + getStateName(state));
            }
            switch (state) {
                case ON_CREATE:
                    mTransactionHandler.handleLaunchActivity(r, mPendingActions,
                            null /* customIntent */);
                    break;
                case ON_START:
                    mTransactionHandler.handleStartActivity(r, mPendingActions);
                    break;
                case ON_RESUME:
                    mTransactionHandler.handleResumeActivity(r.token, false /* finalStateRequest */,
                            r.isForward, "LIFECYCLER_RESUME_ACTIVITY");
                    break;
                case ON_PAUSE:
                    mTransactionHandler.handlePauseActivity(r.token, false /* finished */,
                            false /* userLeaving */, 0 /* configChanges */, mPendingActions,
                            "LIFECYCLER_PAUSE_ACTIVITY");
                    break;
                case ON_STOP:
                    mTransactionHandler.handleStopActivity(r.token, false /* show */,
                            0 /* configChanges */, mPendingActions, false /* finalStateRequest */,
                            "LIFECYCLER_STOP_ACTIVITY");
                    break;
                case ON_DESTROY:
                    mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
                            0 /* configChanges */, false /* getNonConfigInstance */,
                            "performLifecycleSequence. cycling to:" + path.get(size - 1));
                    break;
                case ON_RESTART:
                    mTransactionHandler.performRestartActivity(r.token, false /* start */);
                    break;
                default:
                    throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
            }
        }
    }
  • 這裏注意一個誤區,Activity的啓動計時是以onWindowFocused爲準而不是以onResume爲準,應爲onResume執行完了之後,Activity才添加到Window中並進行進行界面佈局的繪製,至此Activity的啓動基本完成

Activity啓動流程總結

  • Activity啓動過程中其實就是應用進程和系統進程通過AIDL來進行雙向跨進程通信的一個過程
    在這裏插入圖片描述

解決ActivityNotFoundException問題

爲什麼會有ActivityNotFoundException這個異常

  • Activity需要在配置文件中註冊,然後在系統進程中對配置文件中的Activity進行檢測,如果發現Activity沒有註冊就會返回一個錯誤的結果,然後在Instrumentation類的checkStartActivityResult方法中會拋出這個異常
    在這裏插入圖片描述

實現插件化啓動Activity如何解決這個問題?

  • Activity的信息類是被裝在Intent中傳遞給ActivityManagerService中的,然後在ActivityManagerService去對Activity信息進行檢測,如果我們想啓動一個沒有註冊過的Activity可以在應用進程的某一處節點hook掉,把我們要啓動的目標Activity替換成已經註冊好的Activity,這樣在系統進程進行Activity檢測的時候就可以騙過去
  • 系統進程完成Activity檢測後會再回傳到應用進程,然後根據Activity信息來創建Activity類並執行生命週期的方法,所以我們可以在應用進程的某一處節點再hook一下,把Activity類信息替換回我們想要啓動的目標Activity,這樣就可以啓動未註冊過的Activity而不報錯

如何尋找Hook點?

  • 由於我們鎖需要hook的地方都是在應用進程的,所以應用進程的hook我們是可以輕鬆的通過Java反射,動態代理來進行攔截的
  • Hook的原則是靜態變量或者單例對象,儘量Hook pulic的對象和方法,非public不保證每個版本都一樣,需要適配

Activity啓動流程的hook點(針對Android9.0以上)

  • ActivityManager的IActivityManagerSingleton屬性
    在這裏插入圖片描述
    在這裏插入圖片描述
    hook代碼:
Method getServiceMethod = ActivityManager.class.getDeclaredMethod("getService");
        getServiceMethod.setAccessible(true);
        Object iActivityManagerObj = getServiceMethod.invoke(ActivityManager.class, null);

        Field IActivityManagerSingleton = ActivityManager.class.getDeclaredField("IActivityManagerSingleton");
        IActivityManagerSingleton.setAccessible(true);
        Object IActivityManagerSingletonObj = IActivityManagerSingleton.get(ActivityManager.class);

        Class<?> singletonCls = Class.forName("android.util.Singleton");
        Field mInstance = singletonCls.getDeclaredField("mInstance");
        mInstance.setAccessible(true);

        Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager");
        Object iActivityManagerProxy = Proxy.newProxyInstance(ActivityManager.class.getClassLoader()
                , new Class<?>[]{iActivityManagerInterface}, new IActivityManagerHandler(iActivityManagerObj));
        mInstance.set(IActivityManagerSingletonObj, iActivityManagerProxy);
  • 用Java動態代理把Activity信息替換成代理Activity
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5C9W5HaD-1587118235169)(/download/attachments/126756629/%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_dcad1e9d-499a-48b8-a034-cbf08215c2ec.png?version=1&modificationDate=1587079653666&api=v2 '企業微信截圖_dcad1e9d-499a-48b8-a034-cbf08215c2ec.png')]

  • 通過hook Handle的Callback來攔截回傳Activity的信息,並換回我們要啓動的Activity
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-xtU1sAAh-1587118235169)(/download/attachments/126756629/%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_e3953cb0-a42f-44d6-a6db-604666da5dfb.png?version=1&modificationDate=1587079782787&api=v2 '企業微信截圖_e3953cb0-a42f-44d6-a6db-604666da5dfb.png')]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章