Android Frameworks系列(二) 徹底弄懂startActivity
在Android Frameworks系列(一) startService啟動 一文中我們介紹了startService啟動的過程,和startService一樣,startActivity也是以ActivityManagerService為核心工作的。原理也差不多,不過要比startService複雜,因為Activity的生命週期更多,並且還涉及UI方面的工作。為了分析包括zygote孵化目標程式,建立task等過程,就以Launcher啟動應用來說明,另外因為在startService中分析過其詳細的呼叫鏈,本篇不會再詳細的貼出其呼叫鏈的程式碼,只會分析核心方法的功能和程式執行的流程,具體程式碼大家可以參照流程圖具體去分析。
我們知道當使用者點選Launcher應用中對應的應用程式(TargetApp)圖示時,Launcher將啟動TargetApp。具體過程為Launcher通知ActivityManagerService啟動TargetApp的入口Activity,ActivityManagerService發現TargetApp程式還沒有建立,所以通知Zygote去fork一個程式,然後在該程式中經過一系列的操作執行ActivityThread的main方法。在TargetApp準備就緒後會通知ActivityManagerService應用已經啟動,ActivityManagerService會持有TargetApp的一個代理物件,通過這個代理物件ActivityManagerService可以通知TargetApp建立Activity例項,開始Activity的生命週期。
從上圖可以看出整個過程涉及4個程式間通訊。通訊方式其實在Android Frameworks系列(一) startService啟動 一文中已經分析過。用到了Binder和Socket兩種方式。具體可以看一下上文。
我們上邊說了,當使用者點選Launcher上的應用圖示,Launcher會通知ActivityManagerService啟動TargetApp,也就是說,當使用者點選圖示後,經常一系列的操作,會進入到ActivityManagerService的startActivity方法。我們就從startActivity開始分析吧!
在此之前,先認識一下下面幾個看起來非常相似的方法:
ActivityManagerService:startActivityAsUser()
ActivityStarter:startActivityMayWait()
ActivityStarter:startActivityLocked()
ActivityStarter:startActivityUnchecked()
這幾個方法會依次呼叫,並且都有明確的分工。接下來分析他們都做了什麼,和我們平時的開發有什麼關係。
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
ActivityManagerService的startActivityAsUser()方法只比startActivity多了最後一個UserId引數。這個UserId可以通過Binder機制的getCallingUid()方法獲取到。有了這個UserId,ActivityManagerService就可以檢查呼叫者的許可權檢查:
@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
enforceNotIsolatedCaller("startActivity");
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY, "startActivity", null);
// TODO: Switch to user app stacks here.
return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
profilerInfo, null, null, bOptions, false, userId, null, null);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
enforceNotIsolatedCaller(“startActivity”)檢查呼叫者是否被隔離,如果被隔離會丟擲SecurityException。handleIncomingUser()檢查呼叫者是否有許可權執行call操作。
總得來說startActivityAsUser就是做一下呼叫者的基本許可權的檢查。通過之後,就交個ActivityStarter的startActivityMayWait()方法。這個方法很長,但是其功能還是非常清晰的:
相信我們開發Android應用都清楚,start一個Activity時,Intent可以是顯式的,也可以是隱式的。當Intent是顯式的時候,Intent已經有了目標Activity的資訊,但是如果是隱式的話,就需要通過ActivityStackSupervisor的resolveActivity來匹配了:
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
- 1
接著會判斷目標程式是不是重量級程式(heavy-weight),判斷是否已經存在不同於目標程式的重量級程式,如果存在則會重新賦值intent。之後會繼續執行startActivityLocked方法。最後會根據傳入的outResult是否為空來等待是否將執行的結果寫入到outResult中,在原始碼中是一個while迴圈來達到等待的目的,我們在startActivity時傳入的outResult為null,所在在這個過程中不會執行while循序。不過這或許是startActivityMayWait名字中有一個mayWait的原因。接著分析startActivityLocked()方法。
在分析startService一文說過方法名後帶有Locked的一般都是要求同步的,因為方法中會操作臨界資源。
startActivityLocked()方法做的第一件事就是判斷caller程式是否還存在,因為caller程式在發出啟動請求後,被系統kill掉了,所以這裡還是要判斷一下,如果已經kill掉了,直接返回。如果還存活著,接著處理launchFlags,判斷是不是Intent.FLAG_ACTIVITY_FORWARD_RESULT,那麼這個標記是什麼意思呢?舉個開發中可能遇到的需求:
ActivityA啟動ActivityB,ActivityB再啟動ActivityC,當C處理完成後直接返回ActivityA而不是ActivityB,也就是跨越式傳遞。Intent.FLAG_ACTIVITY_FORWARD_RESULT就是達到這個目的而存在的。
當啟動ActivityC的launchFlags是Intent.FLAG_ACTIVITY_FORWARD_RESULT時,需要將caller由ActivityB修改為ActivityA,這樣才能達到ActivityC在處理完事件後setResult時返回到ActivityA。也正是由於這個原因,ActivityB不能以startActivityForResult啟動ActivityC,否則會報
ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT的異常而退出。
接著會判斷是否匹配到Intent的目標,如果沒有匹配到,那麼將不會再處理該Intent,程式直接返回。如果匹配到,那麼接著校驗caller是否有許可權啟動目標Activity。許可權必須全部校驗通過,否則將報SecurityException。以上的判斷結果都將存放在ActivityRecord型別的變數r中,然後繼續執行startActivityUnchecked()方法,該方法涉及一系列的啟動模式,可以結合Android啟動模式之lunchMode 來加深印象。
在startActivityUnchecked方法中開始會執行以下兩個方法:
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);
computeLaunchingTaskFlags();
- 1
- 2
- 3
- 4
setInitialState()方法校驗intent的flag是否是特定的flag
FLAG_ACTIVITY_NO_USER_ACTION這個flag表示不是使用者觸發的啟動Activity,而是由於像來電,鬧鈴等觸發的Activity啟動。
START_FLAG_ONLY_IF_NEEDED表示只有在需要的時候才啟動目標Activity。也就是說如果呼叫者和被啟動的是一個,那麼就沒有必要去進行重複的步驟了。
FLAG_ACTIVITY_NO_ANIMATION表示是否有Animation。
computeLaunchingTaskFlags()主要就是判斷是否需要啟動新的task。其核心程式碼如下:
if (mSourceRecord == null) {
// This activity is not being started from another... in this
// case we -always- start a new task.
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
"Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
} else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
// The original activity who is starting us is running as a single
// instance... this new activity it is starting must go on its
// own task.
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
} else if (mLaunchSingleInstance || mLaunchSingleTask) {
// The activity being started is a single instance... it always
// gets launched into its own task.
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
如果mSourceRecord為null,表明我們應該新開一個task,也就是會設定mLaunchFlags 為FLAG_ACTIVITY_NEW_TASK。如果mSourceRecord的launchMode為LAUNCH_SINGLE_INSTANCE或者目標Activity的啟動模式為singleTask或者singleInstance都需要從新建立一個task。
回到startActivityUnchecked中會繼續處理flag,由於flag有很多,我將挑幾個我們平時可能遇到的講。
1.如果同時設定了FLAG_ACTIVITY_NEW_TASK 和FLAG_ACTIVITY_CLEAR_TASK 那麼將清理整個的task,然後將目標Activity放入task中,如圖ActivityB啟動ActivityC時設定這兩個標誌,那麼最後ActivityA和ActivityB都將被清理。
2.如果設定了FLAG_ACTIVITY_CLEAR_TOP或者launchMode為singleTask、singleInstance。那麼將會清理目標Activity之上的元素。否則如果top元素就是目標Activity那麼將整個task放到最前邊。並且會執行Activity的onNewIntent()方法,這一點相信大家都熟悉。和Android啟動模式之lunchMode 的內容是對應的。
處理這些之後,會執行ActivityStack的startActivityLocked方法。這個方法很關鍵,處理和WindowManagerService之間的互動,保證Activity對應的UI能在螢幕上顯示出來。
首先判斷目標Activity所在的task是不是新建的task,如果不是在新task,那麼還得查詢是哪一個task。接著將目標Activity放到整個stack的最頂層,這樣才能和使用者產生互動。之後還會根據之前是否有FLAG_ACTIVITY_NO_ANIMATION標記來決定是否需要執行切換動畫。我們知道一個Activity的UI能在螢幕上顯示出來,必須通過appToken在WindowManagerService中已經註冊。這個註冊的過程就是在startActivity流程中註冊的,原始碼很簡單:
void addConfigOverride(ActivityRecord r, TaskRecord task) {
final Rect bounds = task.updateOverrideConfigurationFromLaunchBounds();
// TODO: VI deal with activity
mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
(r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges,
task.voiceSession != null, r.mLaunchTaskBehind, bounds, task.mOverrideConfig,
task.mResizeMode, r.isAlwaysFocusable(), task.isHomeTask(),
r.appInfo.targetSdkVersion);
r.taskConfigOverride = task.mOverrideConfig;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
ActivityStack的startActivityLocked方法通過呼叫addConfigOverride方法完成這一操作。執行完這些操作之後,startActivity的一系列準備工作基本完成了,這時會回到ActivityStarter的startActivityUnchecked()方法中繼續進行啟動流程的處理。
ActivityManagerService在以上步驟準備就緒後就要開始resume目標Activity了,不過在這之前需要先pause當前執行的Activity,這裡其實也是涉及到Binder通訊的,不過原理和之前startService中ActivityManagerService同應用通訊的原理是一樣的,這裡不要贅述。在應用pause Activity成功之後,會通知ActivityManagerService,其接收到通知後,會有兩個情況(同startService原理其實類似,可以參考):
第一,如果目標Activity所在的程式已經存在,那麼通過一系列的呼叫,利用ClassLoader將目標Activity,載入到記憶體中來,然後在開始呼叫其onCreate(),onResume()等一系列生命週期方法。這樣Activity就真正的被啟動起來了。
第二,如果目標Activity所在的程式不存在,那麼ActivityManagerService還得通知Zygote程式fork一個程式,之後初始化新程式,之後在載入目標Activity到記憶體中來,最終呼叫到目標Activity的onCreate(),onResume()等一系列生命週期方法,這和第一種情況是一樣的。
總結:startActivity整個流程程式碼呼叫鏈很長,涉及好幾個跨程式通訊。但是其核心的流程還是很清晰的。
1.校驗caller程式是否存在,caller程式是否有許可權啟動目標
2.校驗Intent引數是否合法,匹配Intent所對應的目標程式。
3.根據各種flag和啟動模式,處理源task,目標Activity所在task。
4.向WindowManagerService新增AppToken,處理和WIndowManagerService的通訊
5.根據目標程式是否存在,決定是否通知Zygote孵化程式。之後將目標Activity載入到記憶體中,並執行其生命週期方法。
相關文章
- 徹底弄懂 Nginx location 匹配Nginx
- 徹底弄懂Javascript中的thisJavaScript
- 徹底弄懂 JavaScript 閉包JavaScript
- 帶你徹底弄懂Event LoopOOP
- 徹底弄懂JS原型與繼承JS原型繼承
- 徹底弄懂瀏覽器快取策略瀏覽器快取
- 這次,徹底弄懂介面及抽象類抽象
- 30分鐘徹底弄懂flex佈局Flex
- 這次我讓你徹底弄懂 RESTfulREST
- 一文徹底弄懂ArrayList原始碼原始碼
- 一文徹底弄懂Java的IO操作Java
- Android Frameworks系列(一) startService啟動AndroidFramework
- 徹底弄懂UTF-8、Unicode、寬字元、localeUnicode字元
- 「前端進階」徹底弄懂函式柯里化前端函式
- 轉:徹底弄懂HTTP快取機制及原理HTTP快取
- 徹底弄懂瀏覽器端的Event-Loop瀏覽器OOP
- 徹底弄懂oracle硬解析、軟解析、軟軟解析Oracle
- 一文徹底弄懂MySQL的最佳化MySql
- 這一次,徹底弄懂 JavaScript 執行機制JavaScript
- 一文徹底弄懂Spring IOC 依賴注入Spring依賴注入
- 一文徹底弄懂JUC工具包的Semaphore
- 7000字+24張圖帶你徹底弄懂執行緒池執行緒
- 深入JavaScript系列(四):徹底搞懂thisJavaScript
- 從CPU Cache出發徹底弄懂volatile/synchronized/cas機制synchronized
- [譯]一篇幫你徹底弄懂NodeJs中的BufferNodeJS
- 深入理解JavaScript之徹底弄懂JsEventLoop執行機制JavaScriptJSOOP
- 這一次,徹底弄懂JS執行機制(Event Loop)JSOOP
- 一文徹底弄懂MySQL最佳化之深度分頁MySql
- 從 synchronized 到 CAS 和 AQS – 徹底弄懂 Java 各種併發鎖synchronizedAQSJava
- 從 synchronized 到 CAS 和 AQS - 徹底弄懂 Java 各種併發鎖synchronizedAQSJava
- 一次弄懂Event Loop(徹底解決此類面試問題)OOP面試
- 一文徹底弄懂JUC工具包的CountDownLatch的設計理念與底層原理CountDownLatch
- 原始碼探探之startActivity(二)原始碼
- 徹底弄懂C#中delegate、event、EventHandler、Action、Func的使用和區別C#
- js模組化程式設計之徹底弄懂CommonJS和AMD/CMD!JS程式設計
- 如何繼承Date物件?由一道題徹底弄懂JS繼承。繼承物件JS
- 一文徹底弄懂spring boot自動轉配的過程Spring Boot
- 這一次,徹底弄懂TCP三次握手,四次揮手TCP