深入理解四大組件(二)Android8.0 根 Activity 的啟動過程(下)

上篇鏈接:深入理解四大組件(一)Android8.0 根 Activity 的啟動過程(上)

1. ActivityThread 啟動 Activity 的過程

通過上篇知識點合冀,我們知道目前的代碼邏輯運行在應(yīng)用程序進(jìn)程中府阀。先來查看 ActivityThread 啟動 Activity 過程的時序圖掩宜,如圖所示:

ActivityThread 啟動 Activity 過程的時序圖

接著查看 ApplicationThread 的 scheduleLaunchActivity 方法旦事,其中 ApplicationThread 是 ActivityThread 的內(nèi)部類,應(yīng)用程序進(jìn)程創(chuàng)建后會運行代表主線程的實例 ActivityThread族吻,它管理著當(dāng)前應(yīng)用程序進(jìn)程的主線程帽借。ApplicationThread 的 scheduleLaunchActivity 方法如下所示:
frameworks/base/core/java/android/app/ActivityThread.java

        @Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
                  updateProcessState(procState, false);
                  ActivityClientRecord r = new ActivityClientRecord();
                  r.token = token;
                  r.ident = ident;
                  r.intent = intent;
                  r.referrer = referrer;
                 ···
                  updatePendingConfiguration(curConfig);
                  sendMessage(H.LAUNCH_ACTIVITY, r);
        }

scheduleLaunchActivity 方法將啟動 Activity 的參數(shù)封裝成 ActivityClientRecord珠增,sendMessage 方法向 H 類發(fā)送類型為 LAUNCH_ACTIVITY 的消息,并將 ActivityClientRecord 傳遞過去砍艾,sendMessage 方法有多個重載方法蒂教,最終調(diào)用的 sendMessage 方法如下所示:
frameworks/base/core/java/android/app/ActivityThread.java

    private void sendMessage(int what, Object obj, int arg1, int arg2, int seq) {
        if (DEBUG_MESSAGES) Slog.v(
                TAG, "SCHEDULE " + mH.codeToString(what) + " arg1=" + arg1 + " arg2=" + arg2 +
                        "seq= " + seq);
        Message msg = Message.obtain();
        msg.what = what;
        SomeArgs args = SomeArgs.obtain();
        args.arg1 = obj;
        args.argi1 = arg1;
        args.argi2 = arg2;
        args.argi3 = seq;
        msg.obj = args;
        mH.sendMessage(msg);
    }

這里的 mH 指的是 H,它是 ActivityThread 的內(nèi)部類并繼承自 Handler,是應(yīng)用程序進(jìn)程中主線程的消息管理類脆荷。因為 ApplicationThread 是一個 Binder悴品,它的調(diào)用邏輯運行在 Binder 線程池中,所以這里需要用 H 將代碼的邏輯切換到主線程中简烘。H 的代碼如下所示:
frameworks/base/core/java/android/app/ActivityThread.java

private class H extends Handler {
      public static final int LAUNCH_ACTIVITY         = 100;
      public static final int PAUSE_ACTIVITY          = 101;
...
public void handleMessage(Message msg) {
          if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
          switch (msg.what) {
              case LAUNCH_ACTIVITY: {
                  Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                  final ActivityClientRecord r = (ActivityClientRecord) msg.obj;  //1
                  r.packageInfo = getPackageInfoNoCheck(
                          r.activityInfo.applicationInfo, r.compatInfo);  //2
                  handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");  //3
                  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
              } break;
              case RELAUNCH_ACTIVITY: {
                  Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
                  ActivityClientRecord r = (ActivityClientRecord)msg.obj;
                  handleRelaunchActivity(r);
                  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
              } break;
            ...
}

查看 H 的 handleMessage 方法中對 LAUNCH_ACTIVITY 的處理,在注釋 1 處將傳過來的 msg 的成員變量 obj 轉(zhuǎn)換為 ActivityClientRecord定枷。在 注釋 2 處通過 getPackageInfoNoCheck 方法獲得 LoadedApk 類型的對象并賦值給 ActivityClientRecord 的成員變量 packageInfo孤澎。應(yīng)用程序進(jìn)程要啟動 Activity 時需要將該 Activity 所屬的 APK 加載進(jìn)來,而 LoadApk 就是用來描述已加載的 APK 文件的欠窒。在注釋 3 處調(diào)用 handleLaunchActivity 方法覆旭,代碼如下所示:
frameworks/base/core/java/android/app/ActivityThread.java

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    ...
    WindowManagerGlobal.initialize();
    //啟動Activity
    Activity a = performLaunchActivity(r, customIntent);  //1
    if (a != null) {
        r.createdConfig = new Configuration(mConfiguration);
        reportSizeConfigurations(r);
        Bundle oldState = r.state;
        //將Activity的狀態(tài)置為Resume
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);  //2
        if (!r.activity.mFinished && r.startsNotResumed) {
            performPauseActivityIfNeeded(r, reason);
            if (r.isPreHoneycomb()) {
                r.state = oldState;
            }
        }
    } else {
        try {
            //停止Activity啟動
            ActivityManager.getService()
                .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                        Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
}

注釋 1 處的 performLaunchActivity 方法用來啟動 Activity,注釋 2 處的代碼用來將 Activity 的狀態(tài)設(shè)置為 Resume岖妄。如果該 Activity 為 null 則會通知 AMS 停止啟動 Activity型将。下面來查看 performLaunchActivity 方法做了什么:
frameworks/base/core/java/android/app/ActivityThread.java

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       //獲取ActivityInfo類
       ActivityInfo aInfo = r.activityInfo;  //1
       if (r.packageInfo == null) {
       //獲取APK文件的描述類LoadedApk
           r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                   Context.CONTEXT_INCLUDE_CODE);  //2
       }

       ComponentName component = r.intent.getComponent();  //3
       ...
       //創(chuàng)建要啟動Activity的上下文環(huán)境
       ContextImpl appContext = createBaseContextForActivity(r);  //4
       Activity activity = null;
       try {
           java.lang.ClassLoader cl = appContext.getClassLoader();
           //用類加載器來創(chuàng)建該Activity的實例
           activity = mInstrumentation.newActivity(
                   cl, component.getClassName(), r.intent);  //5
         ...
       } catch (Exception e) {
         ...
       }

       try {
           //創(chuàng)建Application
           Application app = r.packageInfo.makeApplication(false, mInstrumentation);  //6
           ...
           if (activity != null) {
              ...
               /**
               * 7 初始化Activity
               */
               activity.attach(appContext, this, getInstrumentation(), r.token,
                       r.ident, app, r.intent, r.activityInfo, title, r.parent,
                       r.embeddedID, r.lastNonConfigurationInstances, config,
                       r.referrer, r.voiceInteractor, window, r.configCallback);
              ...
               if (r.isPersistable()) {
                   mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);  //8
               } else {
                   mInstrumentation.callActivityOnCreate(activity, r.state);
               }
              ...
           }
           r.paused = true;
           mActivities.put(r.token, r);
       } catch (SuperNotCalledException e) {
           throw e;
       } catch (Exception e) {
         ...
       }
       return activity;
   }

注釋 1 處用來獲取 ActivityInfo,用于儲存代碼以及 AndroidManifes 設(shè)置的 Activity 和 Receiver 節(jié)點信息荐虐,比如 Activity 的 theme 和 launchMode七兜。在注釋 2 處獲取 APK 文件的描述類 LoadedApk。在注釋 3 處獲取要啟動的 Activity 的 ComponentName 類福扬,在 ComponentName 類中保存了該 Activity 的包名和類名腕铸。注釋 4 處用來創(chuàng)建要啟動 Activity 的上下文環(huán)境。注釋 5 處根據(jù) ComponentName 中儲存的 Activity 類名铛碑,用類加載器來創(chuàng)建該 Activity 的實例狠裹。注釋 6 處用來創(chuàng)建 Application,makeApplication 方法內(nèi)部會調(diào)用 Application 的 onCreate 方法汽烦。注釋 7 處調(diào)用 Activity 的 attach 方法初始化 Activity涛菠,在 attach 方法中會創(chuàng)建 Window 對象(PhoneWindow)并與 Activity 自身進(jìn)行關(guān)聯(lián)撇吞。在注釋 8 處調(diào)用 Instrumentation 的 callActivityOnCreate 方法來啟動 Activity俗冻,如下所示:
frameworks/base/core/java/android/app/Instrumentation.java

public void callActivityOnCreate(Activity activity, Bundle icicle,
          PersistableBundle persistentState) {
      prePerformCreate(activity);
      activity.performCreate(icicle, persistentState);  //1
      postPerformCreate(activity);
  }

注釋 1 處調(diào)用了 Activity 的 performCreate 方法,代碼如下所示:
frameworks/base/core/java/android/app/Activity.java

final void performCreate(Bundle icicle, PersistableBundle persistentState) {
      restoreHasCurrentPermissionRequest(icicle);
      onCreate(icicle, persistentState);
      mActivityTransitionState.readState(icicle);
      performCreateCommon();
  }

在 performCreate 方法中調(diào)用 Activity 的 onCreate 方法言疗,講到這里死姚,根 Activity 就啟動了,即應(yīng)用程序就啟動了碰缔。根 Activity 啟動過程就講到這里账劲,下面我們來學(xué)習(xí)根 Activity 啟動過程中涉及的進(jìn)程。

2. 根 Activity 啟動過程中涉及的進(jìn)程

根 Activity 啟動過程中會涉及 4 個進(jìn)程金抡,分別是 Zygote 進(jìn)程瀑焦、Launcher 進(jìn)程、AMS 所在的進(jìn)程(SystemServer進(jìn)程)梗肝、應(yīng)用程序進(jìn)程榛瓮。它們之間的關(guān)系如圖所示:


根 Activity 啟動過程中涉及的進(jìn)程之間的關(guān)系

首先 Launcher 進(jìn)程向 AMS 請求創(chuàng)建根進(jìn)程 Activity,AMS 會判斷根 Activity 所需的應(yīng)用程序進(jìn)程是否存在并啟動巫击,如果不存在就會請求 Zygote 進(jìn)程創(chuàng)建應(yīng)用程序進(jìn)程禀晓。應(yīng)用程序進(jìn)程啟動后,AMS 會請求創(chuàng)建應(yīng)用程序進(jìn)程并啟動根 Activity坝锰。其中步驟 2 采用的是 Socket 通信粹懒,步驟 1 和步驟 4 采用的是 Binder 通信。為了更好理解顷级,下面給出這個 4 個進(jìn)程調(diào)用的時序圖凫乖,如圖所示:


根 Activity 啟動過程中進(jìn)程調(diào)用時序圖

總結(jié)

如果普通 Activity 啟動過程會涉及幾個進(jìn)程呢?答案是兩個愕把,AMS 所在進(jìn)程和應(yīng)用程序進(jìn)程拣凹。實際上理解了根 Activity 的啟動過程(根 Activity 的 onCreate 過程),根 Activity 和普通 Activity 其他生命周期狀態(tài)(比如 onStart恨豁、onResume 等)過程也會很輕松掌握嚣镜,這些知識點都是觸類旁通的。這兩篇的文章講解了根 Activity 的啟動過程橘蜜,下面我們對其他四大組件的啟動過程進(jìn)行學(xué)習(xí)菊匿!

學(xué)習(xí)資料主要來源于《Android進(jìn)階解密》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市计福,隨后出現(xiàn)的幾起案子跌捆,更是在濱河造成了極大的恐慌,老刑警劉巖象颖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件佩厚,死亡現(xiàn)場離奇詭異,居然都是意外死亡说订,警方通過查閱死者的電腦和手機抄瓦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門潮瓶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人钙姊,你說我怎么就攤上這事毯辅。” “怎么了煞额?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵思恐,是天一觀的道長。 經(jīng)常有香客問我膊毁,道長胀莹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任婚温,我火速辦了婚禮嗜逻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘缭召。我一直安慰自己,他們只是感情好逆日,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布嵌巷。 她就那樣靜靜地躺著,像睡著了一般室抽。 火紅的嫁衣襯著肌膚如雪搪哪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天坪圾,我揣著相機與錄音晓折,去河邊找鬼。 笑死兽泄,一個胖子當(dāng)著我的面吹牛漓概,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播病梢,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼胃珍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蜓陌?” 一聲冷哼從身側(cè)響起觅彰,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钮热,沒想到半個月后填抬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡隧期,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年飒责,在試婚紗的時候發(fā)現(xiàn)自己被綠了赘娄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡读拆,死狀恐怖擅憔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情檐晕,我是刑警寧澤暑诸,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站辟灰,受9級特大地震影響个榕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜芥喇,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一西采、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧继控,春花似錦械馆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至冶忱,卻和暖如春尾菇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背囚枪。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工派诬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人链沼。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓默赂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親括勺。 傳聞我的和親對象是個殘疾皇子放可,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

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