Android應用程序內部啓動Activity過程(startActivity)的源代碼分析

上文介紹了Android應用程序的啓動過程,即應用程序默認Activity的啓動過程,一般來說,這種默認Activity是在新的進程和任務中啓動的;本文將繼續分析在應用程序內部啓動非默認Activity的過程的源代碼,這種非默認Activity一般是在原來的進程和任務中啓動的。

《Android系統源代碼情景分析》一書正在進擊的程序員網(http://0xcc0xcd.com)中連載,點擊進入!

        這裏,我們像上一篇文章Android應用程序啓動過程源代碼分析一樣,採用再上一篇文章Android應用程序的Activity啓動過程簡要介紹和學習計劃所舉的例子來分析在應用程序內部啓動非默認Activity的過程。

        在應用程序內部啓動非默認Activity的過程與在應用程序啓動器Launcher中啓動另外一個應用程序的默認Activity的過程大體上一致的,因此,這裏不會像上文Android應用程序啓動過程源代碼分析一樣詳細分析每一個步驟,我們着重關注有差別的地方。

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

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

[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  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是如何隱式地啓動的:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  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一致。

       Step 3. Instrumentation.execStartActivity

       這一步與上一篇文章Android應用程序啓動過程源代碼分析的Step 4一致。

       Step 4. ActivityManagerProxy.startActivity

       這一步與上一篇文章Android應用程序啓動過程源代碼分析的Step 5一致。

       Step 5. ActivityManagerService.startActivity

       這一步與上一篇文章Android應用程序啓動過程源代碼分析的Step 6一致。

       Step 6. ActivityStack.startActivityMayWait

       這一步與上一篇文章Android應用程序啓動過程源代碼分析的Step 7一致。

       Step 7. ActivityStack.startActivityLocked

       這一步與上一篇文章Android應用程序啓動過程源代碼分析的Step 8一致。

       Step 8. ActivityStack.startActivityUncheckedLocked

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

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  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語句不會執行:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  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。

         繼續往下看:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  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語句。

        接着往下執行:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  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文件中:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  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狀態。

        Step 11. 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文件中:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  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),所以下面語句:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  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工程文件:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  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,上面已經介紹的非常清楚了。

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