startActivity()的細節過程可分為七步,首先從用戶單擊圖標開始。
當用戶單擊某個應用圖標后,執行程序會在該圖標的onClick事件中調用startActivity()方法,該方法屬於Activity類的內部方法,然后該方法會調用startActivityForResult(),調用時自動把第二個參數設為-1。所以,startActivity()和startActivityForResult()兩者沒有本質區別。
在forResult方法內部,會調用Instrumentation對象的executeStartActivity()方法,該對象是在ActivityThread創建Activity時創建的,一個應用程序中只有一個Instrumentation對象,每個Activity內部都有一個該對象的引用。Instrumentation可以理解為應用進程的管家,ActivityThread要創建或者暫停某個Activity時,是通過這個"管家"進行的,設置這個管家的好處是可以統計所有的"開銷",開銷的信息保存在"管家"那里。其實正如其名稱所示,Instrumentation就是為了"測量"、"統計",因為管家掌握了所有的"開銷",自然也就具有了統計的功能。當然,Instrumentation類和ActivityThread的分工是有本質區別的,后者就像是這個家里的主人,負責創建這個"家庭",並負責和外界打交道,比如接收AmS的通知等。
在Instrumentation中,則繼續調用AmS的startActivity()方法,從而真正把啟動的意圖傳遞給AmS。Android中IPC調用是同步的,只有當AmS執行完startActivity()方法后才返回,而在此期間,Instrumentation會一直處於線程等待狀態。由於一個應用進程一般只有一個主線程,這就意味着應用主線程在這期間會一直等待,所幸的是AmS執行startActivity()是異步的,基本上會在很短的時間內返回,然后使用異步通知機制,回調caller的onActivityResult()方法並返回執行結果。這就是為什么在AmS的startActivity()參數中需要包含一個IBinder類型的token。該參數來源於Activity對象內部的mToken變量,而這個變量對應的是AmS中的一個HistoryRecord,AmS正是通過HistoryRecord來識別客戶進程中的Activity的。當AmS執行完指定的Activity后,如果caller需要forResult,AmS就會從HistoryRecord中取出HistoryRecord.app.thread變量,該變量的類型是ActivityThread.ApplicationThread子類,並調用該類的scheduleSendResult(r, list)方法,該方法中的r代表了HistoryRecord。ActivityThread收到這個調用后,將根據參數r判斷屬於哪個Activity,然后調用相應Activity的onActivityResult()方法。
運行環境檢查
當AmS收到啟動某個Activity的意圖(intent)后,首先要檢查一下環境,比如Caller是否有啟動的權限、意圖是否對應存在的Activity等。這就相當於去醫院看病時的導醫一樣,先進行一些簡單的檢查,如果不滿足條件,則立即返回。
這些具體的檢查請參照圖10-4所示。包括如下:
首先,根據intent的信息找到匹配的Component信息,這就是為什么intent中可直接指定Activity名稱的原因,AmS會調用系統內部的PackageManager查詢具體的Component名稱。當然,如果沒有Component匹配該intent,則返回失敗。
接着調用startActivityLocked(caller, intent, ...)方法,AmS中有兩個同名該方法,注意其使用的時機和作用,以下篇幅以不同的參數類型表示不同的方法。該方法的后綴是Locked,意思是該方法必須是線程安全的,或者叫不可重入的,即該函數體只能由一個線程調用,另一個線程也要調用該函數時,必須等待前一個線程執行完。
在startActivityLocked(caller, intent, ...)的包含調用中,主要做了以下幾件事情。
檢查當前正在運行的Activity是否和目標Activity一致,如果一致,則直接返回。這就是說,程序員在Activity使用startActivity啟動自己,不會達到重啟當前Activity的目的。
處理INTENT_FLAG_FORWARD_RESULT標志。從代碼的角度來看,這個標志有一個特殊的作用,就是能夠跨Activity傳遞Result。比如A1→A2,此時如果從A2中啟動A3,並且設置的啟動標志為FORWARD_RESULT,那么A3運行時,可以在A3中調用setResult,然后finish(),其結果會從A3直接返回到A1,並且A1會得到A3所set的result。要滿足這種調用,必須使用以下方式啟動。
A1(startActivityForResult) →A2(StartActivity) →A3。注意A2不能使用forResult的方式啟動A3,否則會發生沖突START_FORWARD_AND_REQUEST_CONFLICT。
在此檢查Caller的app是否存在,這種情況發生在發出startActivity命令后,Caller所在的進程被意外殺死,如果是這樣,AmS拒絕繼續往下執行。
檢查Caller是否具備啟動指定Activity的權限。
如果AmS中有IActivityController對象,則通知該Controller進行相應的控制。在一般情況下,mController始終不存在,可能是Android為了某種系統測試而設置的debug開關。
創建一個臨時的HistoryRecord對象,該對象只是為了后面調用過程中的各種對比,不一定會最終被加入到mHistory列表中。
檢查當前是否允許切換Activity,不允許切換的情況一般發生在當前已經暫停的正在執行的Activity,正在等待下一個Activity的啟動時,此時不能進行Activity切換。如果是這樣,則把指定的Activity臨時添加到mPendingActivityLaunches列表中,等待系統恢復后再繼續執行。
判斷mPendingActivityLaunches列表中是否有等待的Activity要啟動,如果有的話,應當先運行這行等待的Activity。
如果等待序列為空,則調用startActivityUncheckedLocked()方法。此時,要啟動Activity已經通過重重檢驗,被確認為是一個"正當"的啟動請求。接下來,AmS就會開始判斷以何種方式來啟動指定的Activity。
找到或創建合適的Task
前面說過,Task的作用是確保Activity按照指定的方式退出,並當用戶按"Back"鍵時按照指定的方式執行下一個Activity。在真正啟動目標Activity之前,AmS先要確定是否需要為目標Activity創建一個新的Task,過程如下。
處理FLAG_ACTIVITY_NO_USER_ACTION標識。在一般情況下,啟動一個Activity時都不使用該標識,如果不包含該標識,AmS會判斷一定的時間內是否有用戶交互。如果沒有用戶交互的話,AmS會通知Activity回調onUserLeaving()方法,然后再回調onPause()方法,如果使用了該標識,說明目標Activity不和用戶交互,所以也就不需要回調onUserLeaving()方法。
確定是否現在就啟動,在一般情況下都是立即啟動。如果立即啟動,就把r.delay Resume為true,意思是不要延遲啟動。
處理FLAG_ACTIVITY_PREVIOUS_IS_TOP標志,這個標識基本無用。接着再處理onlyIfNeed參數,這個參數會和該標志一起使用,不過都是基本無用。
為目標Activity賦予合法的權限。目標Activity在安裝時會指定所需要的權限,此處正是把這些允許的權限添加到緩存中。
根據launchMode變量設置局部變量launchFlags,因為接下來要根據launchFlags決定啟動Activity的方式。注意launchMode和launchFlags是兩碼事,mode是Activity自己聲明的運行方式,它是在AndroidManifest.xml中指定的;而flag是調用者希望以何種方式運行指定的Activity, 是在調用startActivity()時參數intent中指定的。
對NEW_TASK和SINGLE_TASK標識以及SINGLE_INSTANCE模式進行處理。請注意,這兩個標志必須在r.resultTo==0的條件中,即這兩個標識只能用在startActivity()的方法中,而不能用在startActivityForResult()方法中。因為從Task的角度來看,Android認為不同Task之間的Activity是不能傳遞數據的,所以不能使用NEW_TASK標識,但是還要調用forResult()方法。