Android 進階之瞭解源碼——Activity啓動

我們通常說Android有四大組件,也有說法是五大組件,將Intent也算一種,不管是五大還是四大,它們都是Android app開發中最經常打交道的東西,剛開始的時候也都會碰到一些坑,很多時候我們都是搜索資料,看其他人寫的博客來學習和避坑,其實很多博客資料也就是對官方文檔的翻譯,加上一些自己避坑心得的記錄,有些時候即使通過一些資料解決了某個問題,但其實自己也說不出個所以然來,也是雲裏霧裏,只是記住結論就行了,有些時候身邊一些同事會說,做應用層的,不用太深入底層,先把功能需求做好就行了。這話是有一定道理,但我想,他們說的底層到底是哪一層呢?這可能就是很多程序員自嘲爲碼農的原因吧,不管是做什麼開發,如果只是停留在API的熟練使用,即使再幹十年,也只是在搬磚吧,所以我個人認爲,從初級工程師進階,最關鍵的一步就是要了解源碼,要學習源碼,而不是掌握多少流行庫的調用。Read the fucking source code!另外討論一個問題,什麼是底層呢?我認爲涉及到Java的都不能算底層,只有到C/C++代碼那一層才能算底層,因此Framework不能稱爲底層。

這一篇博客就從Activity的啓動流程開始寫吧,這裏以Android 6.0源碼爲例,其他版本或許會有不同。這篇博客力求簡單通俗,給應用的開發帶來一些啓示,因此不會深入底層,不會涉及C/C++層,更不會涉及Linux內核的一些原理,因爲博主也不甚瞭解,掩面而逃……

Activity啓動流程

通常啓動Activity是有三種方式的

  • 點擊桌面上的圖標
  • 在我們當前應用中startActivity
  • Am命令啓動,在進階第一篇中有詳細講到

事實上第一種和第二種是差不多的,Android系統的桌面就是一個應用。那我們就從startActivity方法開始跟蹤

Activity.java

Activity中最終調用的是
這裏寫圖片描述

這裏mParent應該爲空,如不確定,繼續查看mParent不爲空的情況,則調用如下方法

mParent.startActivityFromChild(this, intent, requestCode);

這裏寫圖片描述

Instrumentation.java

由此,可知,最後仍然是調用到Instrumentation類的execStartActivity方法,我們打開該類的源碼,查看execStartActivity方法,內部有三個同名方法,實際上三個方法具體實現都是類似的,我們找到剛剛跟蹤的那個方法

public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent,
        int requestCode, Bundle options, UserHandle user) {
        //……
        try {
            //……
            //通過直觀感受分析,排除一些遍歷檢查之類的代碼,繼續瀏覽,可以發現又一個冠以startActivity名稱的方法
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(),intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
       //…………
 }
ActivityManagerNative.java

我們看到了ActivityManagerNative.getDefault().startActivity方法,從方法名看就能推測出來這是個關鍵方法,那麼繼續跟蹤該方法,打開源碼
這裏寫圖片描述
這是個抽象類,那麼具體實現肯定不是它,最關鍵的一點是看到他繼承於Binder,回顧一下Android的IPC機制AIDL接口,我們知道編寫的AIDL接口文件生成的對應Java類中,內部類Stub就是繼承於Binder的,看到其內部的startActivity方法,可知這裏已經在進行跨進程通信了,到此本進程的調用已經結束,也就是說真正的startActivity方法,是遠程服務端實現的。基於C/S模型,我們可以把以上的這些調用看做是客戶端實現,接下來就要跟蹤到遠程服務端具體實現了。

關於binder的跨進程通信就先不討論了,這裏只需知道ActivityManagerNative.getDefault()調用對應的系統服務是AMS(即ActivityManagerService)就行。

ActivityManagerService.java

這裏寫圖片描述

可以看到,ActivityManagerService正是繼承自ActivityManagerNative,是其具體實現。我們找到startActivity方法,發現內部調用的是startActivityAsUser方法,直接看到該方法實現
這裏寫圖片描述
略過一些檢查方法,可以發現,這裏的關鍵方法是startActivityMayWait,該方法是在ActivityStackSupervisor類中調用的,該類是一個重要的類,我們看可以在聲明處看到一行註釋
這裏寫圖片描述

ActivityStackSupervisor.java

由此註釋可知,這個類是用來管理所有的Activity任務棧的,這裏先繼續跟蹤,進入startActivityMayWait方法,該方法很長,先看參數聲明

這裏寫圖片描述
這裏將對應的一些值做了標記,標黃的一些參數,就是傳的null值,在這個方法中,我們看到下面一行代碼和註釋:

        // Don't modify the client's object!
        intent = new Intent(intent);

由此我們可以推斷,這裏面很多代碼都是對Intent裏面攜帶的一些數據的獲取、轉換、校驗以及修改,所以這裏重新拷貝了一份intent,就是爲了不改變原始的Intent,依據我一些大量查看源碼的經驗,其實查看紛繁複雜的源碼的時候,很多情況下要靠直覺猜測推斷,先有了一個自己的思路,再往下看源碼印證,否則就很容易被複雜的源碼帶歪了,找不到北了,還有重要的一點就是追蹤關鍵方法,不要糾結細節,看不懂的細節先忽略,即使你自己一兩年前寫的代碼,沒有註釋的情況下你回頭看,也不一定能說出每行代碼的作用,更何況Android這麼龐大複雜的源碼,所以看源碼,千萬別糾結,不要有強迫症,學會認方法名,大膽猜測,小心驗證。

繼續往下看,也確實是在做一些Intent數據的處理,其中有個地方可以稍微關注一下:

       // Collect information about the target of the Intent.
        ActivityInfo aInfo =
                resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);

可以進入resolveActivity方法中去看看,這裏和我們後面要講的一個重要地方其實是調用的同一個方法,而ActivityInfo類其實就包含了所有的關於Activity的信息,這裏獲取的aInfo實例其實只是爲了做一個檢查,至於是檢查什麼,註釋也告訴我們了,可以跳過了
這裏寫圖片描述

好了,接下來就看一看startActivityMayWait這個方法中做的最主要的一件事吧
這裏寫圖片描述
從方法名看,就知道應該就是解析意圖的,實際上此處正是用來處理意圖匹配的,可以進入具體實現查看,這裏的AppGlobals.getPackageManager()調用resolveIntent的真實實現其實是在遠程服務PackageManagerService中,需要研究意圖是如何匹配的時候,就可以找到地方去查看了,源碼如下
這裏寫圖片描述

往下查看,還可以看到一些熟悉的地方,比如我在第一篇博客命令工具中提到做activity啓動時間測試的命令adb shell am start -W -S,當我們指定activity啓動後,就會輸出以下一些信息,如果是從應用啓動的,這裏outResult是傳的null,不會走
這裏寫圖片描述
這裏寫圖片描述
好了,還是回到正題吧,接力繼續下去,這個方法中,真正的關代碼是startActivityLocked

int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho,
                    requestCode, callingPid, callingUid, callingPackage,
                    realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
                    componentSpecified, null, container, inTask);

同樣,還是從名稱就能分辨,我們就認準了startActivity關鍵字

startActivityLocked方法又是比較蛋疼了,繼續做各種檢查,並且處理一些錯誤信息,方法體也不短,這些代碼就先略過吧,其實這個方法對於activity啓動流程來講,最主要做的一件事就是爲目標activity創建了一個ActivityRecord。什麼是ActivityRecord呢?我們之前博客提到用adb shell dumpsys activity a命令查看任務棧的一些具體信息的時候,就會輸出一些TaskRecord和ActivityRecord信息,這裏ActivityRecord就包含了即將要被創建的目標Activityd的各種信息。

這裏寫圖片描述

可以看到這裏將各種信息都通過構造方法傳入ActivityRecord中保存起來

繼續找我們的關鍵方法,看到startActivityUncheckedLocked方法名我們可以舒一口氣了,終於不檢查了……
這裏寫圖片描述

啓動模式處理

就先不查看具體方法實現了,這個方法體非常長,但是極具價值,這個方法就是用來處理Activity的啓動模式的,這裏要着重標記一下,當我們要研究Activity四種啓動模式的時候,就要到這裏來查找研究源碼,這裏先繼續主題,暫時略過

繼續查找跟蹤關鍵方法

//…………
ActivityStack targetStack;
//…………
targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);

我們看到,這裏是調用的ActivityStackstartActivityLocked方法,那麼ActivityStack又是什麼呢?看聲明

/**
 * State and management of a single stack of activities.
 */
final class ActivityStack

這裏的ActivityStack也就是我們常常說的,管理Activity的棧。

ActivityStackstartActivityLocked方法不是特別長,它主要是用來配置任務棧的,將目標ActivityRecord放到適當的地方,這裏我們仍然略過,不去研究具體邏輯,到研究Activity啓動模式的時候再回來查看。

繼續找關鍵方法
這裏寫圖片描述

這次就不是start開頭了,叫resumeTopActivitiesLocked。這裏的doResume默認傳的是true,它是在startActivityUncheckedLocked方法參數中設置的,可以回溯查看

這裏寫圖片描述

ActivityStack.java

這個方法從名字看,是用來恢復Activity的,這裏檢查了目標棧是不是在前臺的,如果是就執行兩個參數的resumeTopActivityLocked方法

繼續跟蹤,最終都會調用到resumeTopActivityInnerLocked方法中,這個方法中的代碼也是相當多了,先瀏覽一下,可以發現一行註釋

        // Remember how we'll process this pause/resume situation, and ensure
        // that the state is reset however we wind up proceeding.

由此我們可以猜測到這個方法中該處理上個Activity的暫停狀態了。我們知道生命週期方法中,當前Activity先進入onPause狀態,然後被啓動的Activity纔開始走生命週期方法,當這個Activity展現到前臺之後,前一個Activity的生命週期才繼續走onStop

繼續查看代碼,果然是找到了處理Pause的地方
這裏寫圖片描述

來到startPausingLocked方法查看,有如下代碼

這裏寫圖片描述

可以看到,如果調用者的進程存在,就會走裏面的schedulePauseActivity方法,我們之前是假設從桌面點擊啓動Activity的,那麼桌面進程肯定是存在的,就會走這裏。那麼該方法的具體實現是在哪裏呢?這裏基本可以看出來,是從調用者進程的主線程執行該方法的,那麼我們可以去一個app的主線程類中去查找一下,實際上這裏是一個IPC遠程調用

我們都知道,Java程序是從一個靜態的main方法開始執行的,它是程序的入口,那麼在Android開發中,這個main方法在哪兒呢?這個其實很容易查找到,無論是在eclipse中還是as中,我們通過打斷點調試的方法,可以很容易的查看方法調用棧,從中就可以找到程序的入口,我們這裏就直接找到該類ActivityThread

ActivityThread.java

這裏寫圖片描述
找到Handler中的具體實現
這裏寫圖片描述

一路跟蹤
handlePauseActivity –> performPauseActivity
這裏寫圖片描述

這裏有幾個地方值得深入看一下,performUserLeavingActivity方法是用來處理回調onUserInteraction()onUserLeaveHint()方法的,這兩個方法不知道大家有沒有用過,使用也很簡單,就和其他生命週期方法一樣使用。在處理某些場景的時候是比常規的生命週期方法有用的,它表示的是用戶的主動操作行爲,比如按返回鍵,或者home鍵觸發當前Activity的生命週期,而其他的生命週期方法比如onPause和onStop是不區分主動還是被動的,舉個栗子,比如說正處在你的Activity界面,突然來了一個電話,incall界面就會出現到前臺,你的Activity肯定就會觸發生命週期的方法,但是這種就是典型的被動觸發,不是用戶主動去操作的,那麼他們就不會回調。我們這裏可以看出來,這兩個方法的執行還在onPause之前。

好了,繼續回到正題,看到performPauseActivity
這裏寫圖片描述
這裏實際上就是在執行生命週期方法了,分別處理了onSaveInstanceStateonPause 我們也可以窮根究底的進去看一下
這裏寫圖片描述
在Activity中的具體實現
這裏寫圖片描述

到這裏完成了該Activity的暫停,我們返回到ActivityThread類繼續查看handlePauseActivity方法
這裏寫圖片描述
這裏可以看到,Activity的暫停階段的過程還並未完全結束,又通過遠程調用,將Activity暫停狀態返回給AMS,那麼我們繼續進入ActivityManagerService中,找到activityPaused
這裏寫圖片描述
看到是調用的ActivityStackactivityPausedLocked方法,進入該方法

…………
 final ActivityRecord r = isInStackLocked(token);
        if (r != null) {
            mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
            if (mPausingActivity == r) {
                …………
                completePauseLocked(true);
            }
        …………

關鍵代碼是completePauseLocked(true),調用完這個方法,纔是真正結束了整個暫停階段的一系列工作。進入該方法,查看到關鍵代碼塊

       if (resumeNext) {
            final ActivityStack topStack = mStackSupervisor.getFocusedStack();
            if (!mService.isSleepingOrShuttingDown()) {
                mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
            } else {
                mStackSupervisor.checkReadyForSleepLocked();
                ActivityRecord top = topStack.topRunningActivityLocked(null);
                if (top == null || (prev != null && top != prev)) {
                    // If there are no more activities available to run,
                    // do resume anyway to start something.  Also if the top
                    // activity on the stack is not the just paused activity,
                    // we need to go ahead and resume it to ensure we complete
                    // an in-flight app switch.
                    mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
                }
            }
        }

可以看到,最終再次調用了mStackSupervisor.resumeTopActivitiesLocked方法處理任務棧的問題,這次我們對它內部的調用就已經輕車路熟了,依次調用如下
resumeTopActivitiesLocked -> resumeTopActivityLocked -> resumeTopActivityInnerLocked

再次進入ActivityStack.java

我們再次來到resumeTopActivityInnerLocked方法,這次就有些不同了

…………
if (mResumedActivity != null) {
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
            pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);
        }
…………

mResumedActivity已經沒有了,此時應該爲空了,因爲剛剛暫停了上一個Activity,新的Activity卻還未創建,那麼繼續往下瀏覽

…………
if (next.app != null && next.app.thread != null) {
    …………
    //此處新的應用還未啓動,進程肯定是不存在的,就會走else邏輯
else{
    // Whoops, need to restart this activity!
    …………
    mStackSupervisor.startSpecificActivityLocked(next, true, true);

}

我們看到註釋,即可確定,這裏的startSpecificActivityLocked就是關鍵方法,進入到該方法,發現又一次判斷了進程是否存在,我們進程仍然是不存在的,繼續找到關鍵方法

…………
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);

startProcessLocked從方法名已經可以判斷,這裏就是去啓動目標Activity的進程了,mService就直接是ActivityManagerService,我們進入源碼繼續,發現調用的是參數最多的那個startProcessLocked方法,其實裏面仍然不是真正創建進程的地方
這裏寫圖片描述
這裏只是創建了一個ProcessRecord,它是記錄進程的全部信息的,可以看到聲明

/**
 * Full information about a particular process that
 * is currently running.
 */
final class ProcessRecord

下面繼續調用重載的startProcessLocked方法
這裏寫圖片描述

那麼進程到底是如何被啓動的呢?進入該方法中查看
這裏寫圖片描述

這裏是通過zygote去啓動一個新進程,實際上就是利用Linux的fork機制創建新進程,並調用該進程的Java入口main函數。我們知道在Linux下,是可以調用fork函數創建新進程的。

到這裏可以舒一口氣了,目標app進程終於被創建出來了,接下來在次進入ActivityThread類中,這次我們從Java的main函數開始了,終於找到了一點Java程序的感覺,有木有……?

繼續看關鍵代碼

public static void main(String[] args) {
        ……………………
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        ……………………

這裏new了一個ActivityThread,創建了我們的主線程,也就是通常說的UI線程,這裏還有一個重要的東西,Looper.prepareMainLooper(),這次就不討論了,下次分析Handler機制的時候再說,這裏的關鍵代碼其實是thread.attach(false)

在Android中,創建一個Activity並不是自己去new出來,Activity的創建都交給系統去統一管理調度的,也是說我們接下來的創建階段,肯定不是自己玩自己的就行了,而是需要交給AMS去統一管理,因此,擺在眼前的事實就是我們要到attach方法中找到遠程調用的相關邏輯,有了這個猜測就好看代碼了

 private void attach(boolean system) {
           ……………………
            final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                // Ignore
            }
………………
}

果然,這裏正好有一個遠程調用,將一個ApplicationThread引用交給了遠程服務端,我們進入AMS中看看具體實現

    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid);
            Binder.restoreCallingIdentity(origId);
        }
    }
應用Application的創建

繼續進入到attachApplicationLocked方法中查看,該方法體代碼較多,但是結合註釋和一些猜測,並不難理解,裏面其實是做了一些預處理,繼續瀏覽看到如下代碼

 thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                    profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                    app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked());

這裏有一個叫bindApplication的方法,看名字,應該進去看一下,這裏的thread就是我們之前的attach方法參數中傳入的ApplicationThread對象,那麼我們找到具體實現看一下

…………
sendMessage(H.BIND_APPLICATION, data);

找到handler中對於的具體實現

         case BIND_APPLICATION:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                    AppBindData data = (AppBindData)msg.obj;
                    handleBindApplication(data);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
進入ActivityThread.java

繼續找到真正的實現handleBindApplication方法,這個方法代碼很多,快速瀏覽一下

private void handleBindApplication(AppBindData data) {
      …………
      final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
      …………
      if (data.instrumentationName !=null) {
          …………
          ApplicationInfo instrApp = new ApplicationInfo();
          …………
          try {
                java.lang.ClassLoader cl = instrContext.getClassLoader();
                mInstrumentation = (Instrumentation)cl.loadClass(data.instrumentationName.getClassName()).newInstance();
           } 
         …………
      } else {
         mInstrumentation =new Instrumentation();
      }
      …………
       // If the app is being launched for full backup or restore, bring it up in
       // a restricted environment with the base application class.
       Application app = data.info.makeApplication(data.restrictedBackupMode, null);
       mInitialApplication = app;
       …………
       try {
            mInstrumentation.onCreate(data.instrumentationArgs);
       }
       …………

       try {
            mInstrumentation.callApplicationOnCreate(app);
       }
…………
}

發現在這個方法中相關的處理還不少,程序的Context、Application以及Instrumentation都是在這裏創建的,連Application的onCreate方法都是在這裏回調的,這個地方要記住。現在我們回到剛剛的attachApplicationLocked方法,繼續往下看

…………
      // See if the top visible activity is waiting to run in this process...
        if (normalMode) {
            try {
                if (mStackSupervisor.attachApplicationLocked(app)) {
                    didSomething = true;
                }
            } catch (Exception e) {
                Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
                badApp = true;
            }
        }
…………

從註釋很容易發現mStackSupervisor.attachApplicationLocked是關鍵代碼,我們進入到ActivityStackSupervisorattachApplicationLocked方法
這裏寫圖片描述

這裏遍歷是要找到位於頂端的棧,並取出目標ActivityRecord對象,可以看到這裏的app爲空,因爲我們很早之前就創建了ActivityRecord,但當時進程並沒被創建出來,後續也還未關聯到進程,所以一直是空,看到這裏的關鍵方法realStartActivityLocked,看到這個名字,感覺長征快要結束了,代碼都要看吐了,有木有……?

進到realStartActivityLocked裏面,代碼又是又臭又長,不過先別急,還是繼續找關鍵代碼,快速瀏覽

…………
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                    new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
                    task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
                    newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
…………

這裏的關鍵代碼是scheduleLaunchActivity,調用再次回到了應用的進程,我們進入ActivityThread中查看具體實現

…………
ActivityClientRecord r = new ActivityClientRecord();
…………
sendMessage(H.LAUNCH_ACTIVITY, r);

這裏創建了一個客戶端的ActivityRecord,真正的實現仍然是在Handler裏,我們繼續跟蹤

…………
case LAUNCH_ACTIVITY: {
     …………
     handleLaunchActivity(r, null);
}

handleLaunchActivity -> performLaunchActivity

performLaunchActivity方法就是真正的實現了,在這個方法裏,終於看到了我們想要看到的,整個流程到此基本結束了,西天取經不容易啊!

…………
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
        …………
        }

原來我們的Activity是反射創建的,往下就是生命週期的方法了,沒什麼好說了
這裏寫圖片描述

至於Activity的顯示,則要回到handleLaunchActivity方法往下看,找到handleResumeActivity方法,這裏就不細看了,只有一個地方多說一下,那就是上圖中調用的activityattach方法,正是在這個方法中將全局ContextPhoneWindowFragmentManager等等關聯到自己的。

啓動流程歸納總結

相關概念

ActivityRecord:記錄着Activity信息,相當於一個Activity
TaskRecord:記錄着task信息
ActivityStack:管理Activity的棧

盜一張圖描述它們之間的關係
這裏寫圖片描述

啓動流程時序圖

  • 在客戶端應用中發起
Created with Raphaël 2.1.2ActivityActivityIntrumentationIntrumentationActivityManagerProxyActivityManagerProxyActivityManagerServiceActivityManagerServicestartActivitystartActivityForResultexecStartActivitystartActivity
  • 在服務端控制邏輯
    1.準備階段
Created with Raphaël 2.1.2ActivityManagerServiceActivityManagerServiceActivityStackSupervisorActivityStackSupervisorActivityStackActivityStackstartActivitystartActivityAsUserstartActivityMayWaitstartActivityLockedstartActivityUncheckedLockedresumeTopActivitiesLockedresumeTopActivityLockedresumeTopActivityInnerLocked

2.暫停上一個Activity

Created with Raphaël 2.1.2ActivityStackActivityStackActivityThreadActivityThreadInstrumentationInstrumentationActivityActivityActivityManagerServiceActivityManagerServicestartPausingLockedschedulePauseActivityhandlePauseActivityperformPauseActivitycallActivityOnPauseperformPauseactivityPausedactivityPausedLockedcompletePauseLocked

3.啓動目標進程

Created with Raphaël 2.1.2ActivityStackActivityStackActivityStackSupervisorActivityStackSupervisorActivityManagerServiceActivityManagerServiceActivityThreadActivityThreadresumeTopActivityInnerLockedstartSpecificActivityLockedstartProcessLockedmain

4.創建目標Activity

Created with Raphaël 2.1.2ActivityThreadActivityThreadActivityManagerServiceActivityManagerServiceActivityStackSupervisorActivityStackSupervisormainattachattachApplicationattachApplicationLockedrealStartActivityLockedscheduleLaunchActivityhandleLaunchActivityperformLaunchActivity

5.顯示

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