activity 啟動流程分析

yangxi_001發表於2013-11-21

上文介紹了Android應用程式的啟動過程,即應用程式預設Activity的啟動過程,一般來說,這種預設Activity是在新的程式和任務中啟動的;本文將繼續分析在應用程式內部啟動非預設Activity的過程的原始碼,這種非預設Activity一般是在原來的程式和任務中啟動的。

        這裡,我們像上一篇文章Android應用程式啟動過程原始碼分析一樣,採用再上一篇文章Android應用程式的Activity啟動過程簡要介紹和學習計劃所舉的例子來分析在應用程式內部啟動非預設Activity的過程。

        在應用程式內部啟動非預設Activity的過程與在應用程式啟動器Launcher中啟動另外一個應用程式的預設Activity的過程大體上一致的,因此,這裡不會像上文Android應用程式啟動過程原始碼分析一樣詳細分析每一個步驟,我們著重關注有差別的地方。

        回憶一下Android應用程式的Activity啟動過程簡要介紹和學習計劃一文所用的應用程式Activity,它包含兩個Activity,分別是MainActivity和SubActivity,前者是應用程式的預設Activity,後者是非預設Activity。MainActivity啟動起來,通過點選它介面上的按鈕,便可以在應用程式內部啟動SubActivity。

        我們先來看一下應用程式的配置檔案AndroidManifest.xml,看看這兩個Activity是如何配置的:

view plain

1.      <?xml version="1.0" encoding="utf-8"?>    

2.      <manifest xmlns:android="http://schemas.android.com/apk/res/android"    

3.          package="shy.luo.activity"    

4.          android:versionCode="1"    

5.          android:versionName="1.0">    

6.          <application android:icon="@drawable/icon" android:label="@string/app_name">    

7.              <activity android:name=".MainActivity"    

8.                        android:label="@string/app_name">    

9.                  <intent-filter>    

10.                    <action android:name="android.intent.action.MAIN" />    

11.                    <category android:name="android.intent.category.LAUNCHER" />    

12.                </intent-filter>    

13.            </activity>    

14.            <activity android:name=".SubActivity"    

15.                      android:label="@string/sub_activity">    

16.                <intent-filter>    

17.                    <action android:name="shy.luo.activity.subactivity"/>    

18.                    <category android:name="android.intent.category.DEFAULT"/>    

19.                </intent-filter>    

20.            </activity>    

21.        </application>    

22.    </manifest>    

        這裡可以很清楚地看到,MainActivity被配置成了應用程式的預設Activity,而SubActivity可以通過名稱“shy.luo.activity.subactivity”隱式地啟動,我們來看一下src/shy/luo/activity/MainActivity.java檔案的內容,可以清楚地看到SubActivity是如何隱式地啟動的:

view plain

1.      public class MainActivity extends Activity  implements OnClickListener {    

2.          ......    

3.          

4.          @Override    

5.          public void onClick(View v) {    

6.              if(v.equals(startButton)) {    

7.                  Intent intent = new Intent("shy.luo.activity.subactivity");    

8.                  startActivity(intent);    

9.              }    

10.        }    

11.    }    

       這裡,首先建立一個名稱為“shy.luo.activity.subactivity”的Intent,然後以這個Intent為引數,通過呼叫startActivity函式來實現隱式地啟動SubActivity。

       有了這些背景知識後,我們就來看一下SubActivity啟動過程的序列圖:

點選檢視大圖


      與前面介紹的MainActivity啟動過程相比,這裡少了中間建立新的程式的步驟;接下來,我們就詳細分析一下SubActivity與MainActivity啟動過程中有差別的地方,相同的地方請參考Android應用程式啟動過程原始碼分析一文。

       Step 1.Activity.startActivity

       這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 2大體一致,通過指定名稱“shy.luo.activity.subactivity”來告訴應用程式框架層,它要隱式地啟動SubActivity。所不同的是傳入的引數intent沒有Intent.FLAG_ACTIVITY_NEW_TASK標誌,表示這個SubActivity和啟動它的MainActivity執行在同一個Task中。

       Step 2.Activity.startActivityForResult

       這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 3一致。

       Step3. Instrumentation.execStartActivity

       這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 4一致。

       Step4. ActivityManagerProxy.startActivity

       這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 5一致。

       Step5. ActivityManagerService.startActivity

       這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 6一致。

       Step6. ActivityStack.startActivityMayWait

       這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 7一致。

       Step7. ActivityStack.startActivityLocked

       這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 8一致。

       Step8. ActivityStack.startActivityUncheckedLocked

       這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 9有所不同,主要是當前要啟動的Activity與啟動它的Activity是在同一個Task中執行的,我們來詳細看一下。這個函式定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java檔案中:

view plain

1.      public class ActivityStack {  

2.        

3.          ......  

4.        

5.          final int startActivityUncheckedLocked(ActivityRecord r,  

6.                 ActivityRecord sourceRecord, Uri[] grantedUriPermissions,  

7.                 int grantedMode, boolean onlyIfNeeded, boolean doResume) {  

8.              final Intent intent = r.intent;  

9.              final int callingUid = r.launchedFromUid;  

10.      

11.            int launchFlags = intent.getFlags();  

12.      

13.            ......  

14.      

15.            if (sourceRecord == null) {  

16.               ......  

17.            } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {  

18.               ......  

19.            } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE  

20.               ......  

21.            }  

22.      

23.            if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {  

24.               ......  

25.            }  

26.      

27.            boolean addingToTask = false;  

28.            if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&  

29.               (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)  

30.               || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK  

31.               || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {  

32.                ......  

33.            }  

34.      

35.            if (r.packageName != null) {  

36.               // If the activity being launched is the same as the one currently  

37.               // at the top, then we need to check if it should only be launched  

38.               // once.  

39.               ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);  

40.               if (top != null && r.resultTo == null) {  

41.                   if (top.realActivity.equals(r.realActivity)) {  

42.                       ......  

43.                   }  

44.               }  

45.      

46.            } else {  

47.               ......  

48.            }  

49.      

50.            boolean newTask = false;  

51.      

52.            // Should this be considered a new task?  

53.            if (r.resultTo == null && !addingToTask  

54.               && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {  

55.                ......  

56.      

57.            } else if (sourceRecord != null) {  

58.                ......  

59.                // An existing activity is starting this new activity, so we want  

60.                // to keep the new one in the same task as the one that is starting  

61.                // it.  

62.                r.task = sourceRecord.task;  

63.                ......  

64.      

65.            } else {  

66.               ......  

67.            }  

68.      

69.            ......  

70.      

71.            startActivityLocked(r, newTask, doResume);  

72.            return START_SUCCESS;  

73.        }  

74.      

75.        ......  

76.      

77.    }  

        這裡,引數intent的標誌位Intent.FLAG_ACTIVITY_NEW_TASK沒有設定,在配置檔案AndriodManifest.xml中,SubActivity也沒有配置啟動模式launchMode,於是它就預設標準模式,即ActivityInfo.LAUNCH_MULTIPLE,因此,下面if語句不會執行:

view plain

1.         if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&  

2.             (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)  

3.             || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK  

4.      || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {  

5.      ......  

6.         }  

        於是,變數addingToTask為false。

         繼續往下看:

view plain

1.         if (r.packageName != null) {  

2.      // If the activity being launched is the same as the one currently  

3.      // at the top, then we need to check if it should only be launched  

4.      // once.  

5.             ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);  

6.      if (top != null && r.resultTo == null) {  

7.           if (top.realActivity.equals(r.realActivity)) {  

8.          ......  

9.           }  

10.    }  

11.      

12.       }   

        這裡看一下當前要啟動的Activity是否就是當前堆疊頂端的Activity,如果是的話,在某些情況下,就不用再重新啟動了。函式topRunningNonDelayedActivityLocked返回當前
堆疊頂端的Activity,這裡即為MainActivity,而當前要啟動的Activity為SubActivity,因此,這二者不相等,於是跳過裡面的if語句。

        接著往下執行:

view plain

1.         // Should this be considered a new task?  

2.         if (r.resultTo == null && !addingToTask  

3.      && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {  

4.      ......  

5.        

6.         } else if (sourceRecord != null) {  

7.      ......  

8.      // An existing activity is starting this new activity, so we want  

9.      // to keep the new one in the same task as the one that is starting  

10.    // it.  

11.    r.task = sourceRecord.task;  

12.    ......  

13.      

14.       } else {  

15.    ......  

16.       }  

        前面說過引數intent的標誌位Intent.FLAG_ACTIVITY_NEW_TASK沒有設定,而這裡的sourceRecord即為當前執行啟動Activity操作的Activity,這裡即為MainActivity,因此,它不為null,於是於MainActivity所屬的Task設定到r.task中去,這裡的r即為SubActivity。看到這裡,我們就知道SubActivity要和MainActivity執行在同一個Task中了,同時,變數newTask的值為false。

        最後,函式進 入startActivityLocked(r,newTask, doResume)進一步處理了。這個函式同樣是定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java檔案中:

view plain

1.      public class ActivityStack {  

2.        

3.          ......  

4.        

5.          private final void startActivityLocked(ActivityRecord r, boolean newTask,  

6.                  boolean doResume) {  

7.              final int NH = mHistory.size();  

8.        

9.              int addPos = -1;  

10.      

11.            if (!newTask) {  

12.                // If starting in an existing task, find where that is...  

13.                boolean startIt = true;  

14.                for (int i = NH-1; i >= 0; i--) {  

15.                    ActivityRecord p = (ActivityRecord)mHistory.get(i);  

16.                    if (p.finishing) {  

17.                        continue;  

18.                    }  

19.                    if (p.task == r.task) {  

20.                        // Here it is!  Now, if this is not yet visible to the  

21.                        // user, then just add it without starting; it will  

22.                        // get started when the user navigates back to it.  

23.                        addPos = i+1;  

24.                        if (!startIt) {  

25.                            mHistory.add(addPos, r);  

26.                            r.inHistory = true;  

27.                            r.task.numActivities++;  

28.                            mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,  

29.                                r.info.screenOrientation, r.fullscreen);  

30.                            if (VALIDATE_TOKENS) {  

31.                                mService.mWindowManager.validateAppTokens(mHistory);  

32.                            }  

33.                            return;  

34.                        }  

35.                        break;  

36.                    }  

37.                    if (p.fullscreen) {  

38.                        startIt = false;  

39.                    }  

40.                }  

41.            }  

42.      

43.            ......  

44.      

45.            // Slot the activity into the history stack and proceed  

46.            mHistory.add(addPos, r);  

47.            r.inHistory = true;  

48.            r.frontOfTask = newTask;  

49.            r.task.numActivities++;  

50.      

51.            ......  

52.      

53.            if (doResume) {  

54.                resumeTopActivityLocked(null);  

55.            }  

56.        }  

57.      

58.        ......  

59.      

60.    }  

        這裡傳進來的引數newTask為false,doResume為true。當newTask為false,表示即將要啟動的Activity是在原有的Task執行時,如果這個原有的Task當前對使用者不可見時,這時候就不需要繼續執行下去了,因為即使把這個Activity啟動起來,使用者也看不到,還不如先把它儲存起來,等到下次這個Task對使用者可見的時候,再啟動不遲。這裡,這個原有的Task,即執行MainActivity的Task當前對使用者是可見的,因此,會繼續往下執行。

        接下去執行就會把這個SubActivity通過mHistroy.add(addPos, r)新增到堆疊頂端去,然後呼叫resumeTopActivityLocked進一步操作。

        Step 9.ActivityStack.resumeTopActivityLocked

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 10一致。 

        但是要注意的是,執行到這個函式的時候,當前處於堆疊頂端的Activity為SubActivity,ActivityStack的成員變數mResumedActivity指向MainActivity。
        Step 10. ActivityStack.startPausingLocked
        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 11一致。

        從這裡開始,ActivityManagerService通知MainActivity進入Paused狀態。

        Step11. ApplicationThreadProxy.schedulePauseActivity

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 12一致。

        Step 12.ApplicationThread.schedulePauseActivity

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 13一致。

        Step 13.ActivityThread.queueOrSendMessage
        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 14一致。

        Step 14.H.handleMessage

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 15一致。

        Step 15.ActivityThread.handlePauseActivity

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 16一致。

        Step 16.ActivityManagerProxy.activityPaused

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 17一致。

        Step 17.ActivityManagerService.activityPaused

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 18一致。

        Step 18.ActivityStack.activityPaused

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 19一致。

        Step 19.ActivityStack.completePauseLocked

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 20一致。

        執行到這裡的時候,MainActivity就進入Paused狀態了,下面就開始要啟動SubActivity了。

        Step 20.ActivityStack.resumeTopActivityLokced

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 21一致。

        Step 21.ActivityStack.startSpecificActivityLocked

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 22就有所不同了,這裡,它不會呼叫mService.startProcessLocked來建立一個新的程式來啟動新的Activity,我們來看一下這個函式的實現,這個函式定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java檔案中:

view plain

1.      public class ActivityStack {  

2.        

3.          ......  

4.        

5.          private final void startSpecificActivityLocked(ActivityRecord r,    

6.                  boolean andResume, boolean checkConfig) {    

7.              // Is this activity's application already running?    

8.              ProcessRecord app = mService.getProcessRecordLocked(r.processName,    

9.                      r.info.applicationInfo.uid);    

10.      

11.            ......    

12.      

13.            if (app != null && app.thread != null) {    

14.                try {    

15.                    realStartActivityLocked(r, app, andResume, checkConfig);    

16.                    return;    

17.                } catch (RemoteException e) {    

18.                    ......    

19.                }    

20.            }    

21.      

22.            ......  

23.      

24.        }    

25.      

26.        ......  

27.      

28.    }  

        這裡由於不是第一次啟動應用程式的Activity(MainActivity是這個應用程式第一個啟動的Activity),所以下面語句:

view plain

1.      ProcessRecord app = mService.getProcessRecordLocked(r.processName,    

2.      nfo.applicationInfo.uid);    

        取回來的app不為null。在上一篇文章Android應用程式啟動過程原始碼分析中,我們介紹過,在Activity應用程式中的AndroidManifest.xml配置檔案中,我們沒有指定application標籤的process屬性,於是系統就會預設使用package的名稱,這裡就是"shy.luo.activity"了。每一個應用程式都有自己的uid,因此,這裡uid + process的組合就可以建立一個全域性唯一的ProcessRecord。這個ProcessRecord是在前面啟動MainActivity時建立的,因此,這裡將它取回來,並儲存在變數app中。注意,我們也可以在AndroidManifest.xml配置檔案中指定SubActivity的process屬性值,這樣SubActivity就可以在另外一個程式中啟動,不過很少有應用程式會這樣做,我們不考慮這種情況。

        這個app的thread也是在前面啟動MainActivity時建立好的,於是,這裡就直接呼叫realStartActivityLocked函式來啟動新的Activity了,新的Activity的相關資訊都儲存在引數r中了。

        Step 22.ActivityStack.realStartActivityLocked

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 28一致。
        Step 23.ApplicationThreadProxy.scheduleLaunchActivity

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 29一致。

        Step 24.ApplicationThread.scheduleLaunchActivity

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 30一致。

        Step 25.ActivityThread.queueOrSendMessage

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 31一致。

        Step 26.H.handleMessage

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 32一致。

        Step 27.ActivityThread.handleLaunchActivity

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 33一致。

        Step 28.ActivityThread.performLaunchActivity

        這一步與上一篇文章Android應用程式啟動過程原始碼分析的Step 34一致,不過,這裡要從ClassLoader裡面載入的類就是shy.luo.activity.SubActivity了。

        Step 29.SubAcitiviy.onCreate

        這個函式定義在packages/experimental/Activity/src/shy/luo/activity/SubActivity.java檔案中,這是我們自定義的app工程檔案:

view plain

1.      public class SubActivity extends Activity implements OnClickListener {  

2.        

3.          ......  

4.        

5.          @Override  

6.          public void onCreate(Bundle savedInstanceState) {  

7.              ......  

8.        

9.              Log.i(LOG_TAG, "Sub Activity Created.");  

10.        }  

11.      

12.        ......  

13.      

14.    }  

       這樣,SubActivity就在應用程式Activity內部啟動起來了。
      在應用程式內部啟動新的Activity的過程要執行很多步驟,但是整體來看,主要分為以下四個階段:

       一. Step 1 - Step 10:應用程式的MainActivity通過Binder程式間通訊機制通知ActivityManagerService,它要啟動一個新的Activity;
       二. Step 11 - Step 15:ActivityManagerService通過Binder程式間通訊機制通知MainActivity進入Paused狀態;
       三. Step 16 - Step 22:MainActivity通過Binder程式間通訊機制通知ActivityManagerService,它已經準備就緒進入Paused狀態,於是ActivityManagerService就準備要在MainActivity所在的程式和任務中啟動新的Activity了;
       四. Step 23 - Step 29:ActivityManagerService通過Binder程式間通訊機制通知MainActivity所在的ActivityThread,現在一切準備就緒,它可以真正執行Activity的啟動操作了。

       和上一篇文章Android應用程式啟動過程原始碼分析中啟動應用程式的預設Activity相比,這裡在應用程式內部啟動新的Activity的過程少了中間建立新的程式這一步,這是因為新的Activity是在已有的程式和任務中執行的,無須建立新的程式和任務。

      這裡同樣不少地方涉及到了Binder程式間通訊機制,相關資料請參考Android程式間通訊(IPC)機制Binder簡要介紹和學習計劃一文。

      這裡希望讀者能夠把本文和上文Android應用程式啟動過程原始碼分析仔細比較一下應用程式的預設Activity和非預設Activity啟動過程的不同之處,以加深對Activity的理解。

      最後,在本文和上文中,我們多次提到了Android應用程式中任務(Task)的概念,它既不是我們在Linux系統中所理解的程式(Process),也不是執行緒(Thread),它是使用者為了完成某個目標而需要執行的一系列操作的過程的一種抽象。這是一個非常重要的概念,它從使用者體驗的角度出發,界定了應用程式的邊界,極大地方便了開發者利用現成的元件(Activity)來搭建自己的應用程式,就像搭積木一樣,而且,它還為應用程式遮蔽了底層的程式,即一個任務中的Activity可以都是執行在同一個程式中,也中可以執行在不同的程式中。Android應用程式中的任務的概念,具體可以參考官方文件http://developer.android.com/guide/topics/fundamentals/tasks-and-back-stack.html,上面已經介紹的非常清楚了。

相關文章