activity 啟動流程分析
上文介紹了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是如何配置的:
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是如何隱式地啟動的:
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檔案中:
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語句不會執行:
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。
繼續往下看:
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語句。
接著往下執行:
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檔案中:
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檔案中:
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),所以下面語句:
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工程檔案:
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,上面已經介紹的非常清楚了。
相關文章
- Activity啟動流程分析
- Activity啟動流程原始碼分析原始碼
- Android Activity啟動流程原始碼分析Android原始碼
- Android原始碼分析:Activity啟動流程Android原始碼
- Activity啟動流程分析記錄(Api26)API
- 庖丁解牛 Activity 啟動流程
- 從Activity的啟動流程理解Binder
- Activity啟動過程分析
- app啟動流程,activity啟動流程時序圖,binder相關資料APP時序圖
- 【Android原始碼】Activity的啟動流程Android原始碼
- Activity的起步流程分析
- Activity 的 "啟動流程"(基於 Android 9.0)Android
- 基於8.0原始碼解析:Activity啟動流程原始碼
- 圖解Activity啟動流程,進階高階圖解
- Activity 啟動流程學習總結(附原始碼流程圖)原始碼流程圖
- Activity啟動分析(一)--ActivityThreadthread
- Unbound啟動流程分析
- 深入理解Activity啟動流程和AMS框架(一)框架
- 深入理解Activity啟動流程和AMS框架(二)框架
- 深入理解Activity啟動流程和AMS框架(三)框架
- App 竟然是這樣跑起來的 —— Android App/Activity 啟動流程分析APPAndroid
- Activity啟動分析(二)--建立Window和ViewView
- 原始碼閱讀之Activity啟動與App啟動流程 - Android 9.0原始碼APPAndroid
- 原始碼閱讀之Activity啟動與App啟動流程 – Android 9.0原始碼APPAndroid
- FlutterApp啟動流程分析FlutterAPP
- nodejs啟動流程分析NodeJS
- Android 8.0 原始碼分析 (四) Activity 啟動Android原始碼
- Activity啟動模式模式
- Flutter啟動流程原始碼分析Flutter原始碼
- Linux:uboot啟動流程分析Linuxboot
- apiserver原始碼分析——啟動流程APIServer原始碼
- 深入理解Android 之 Activity啟動流程(Android 10)Android
- activity的啟動模式模式
- 【系統之音】Activity啟動流程——史上最全最簡潔易懂版
- Android之Activity啟動流程詳解(基於api28)AndroidAPI
- Android應用啟動流程分析Android
- Flutter系列三:Flutter啟動流程分析Flutter
- Tomcat原始碼分析--啟動流程Tomcat原始碼