序言
很多人都會用Activity、Window、View,但是你知道他們是怎樣加載出來並呈現在你眼前的嗎?你知道他們之間有着鮮爲人知的關係嗎?
講個很簡單的例子,這一天天氣甚好,小明外出寫生,小明背了一包東西,畫板啊,紙啊,筆啊什麼的,然後小明找了一處風景甚好的地方,從包裏拿出畫板,紙,筆然後開始畫畫,不一會兒小明就畫完了一幅風景圖。在這個例子當中,畫板就好比Activity
,紙就好比Window
,而筆就是View
,我們所看到的就是這幅畫,是通過筆一點一點畫出來的,在哪裏畫呢?當然是紙上了,而最終承載這幅畫的東西就是畫板了。這麼說可能不太生動,下面,我們從源碼的角度來看看這三者的關係。
Activity的創建過程
我們都知道,Activity啓動的時候是從ActivityThread中的Handler中發起的,然後經過handlerLauncher等一系列方法,如果還不知道的話可以去參考我之前寫的:https://github.com/24Kshign/Android-Knowledge/blob/a8b38bf4765df13fee3a573249a1dc472f3a7e6c/Android%E6%BA%90%E7%A0%81%E7%9B%B8%E5%85%B3/Activity%E7%9A%84%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B.md
1ActivityThread類: 2 3private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { 4 5 ... 6 WindowManagerGlobal.initialize(); 7 Activity a = performLaunchActivity(r, customIntent); 8 ... 9}
在這裏先調用了WindowManagerGlobal
中的初始化方法初始化了WindowManagerService
,看名字大概就能知道這是一個WindowManager
的服務,通過這個服務可以對頁面進行操作;然後通過調用performLaunchActivity
方法生成了一個Activity。
Window的創建過程
上面通過performLaunchActivity
方法生成了一個Activity,我們來看看是怎樣生成的:
1ActivityThread類: 2 3private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { 4 5 ... 6 Activity activity = null; 7 try { 8 activity = mInstrumentation.newActivity(cl, 9 component.getClassName(), r.intent); 10 } catch (Exception e) { 11 ... 12 } 13 ... 14 15 if (activity != null) { 16 activity.attach(appContext, this, getInstrumentation(), r.token, 17 r.ident, app, r.intent, r.activityInfo, title, r.parent, 18 r.embeddedID, r.lastNonConfigurationInstances, config, 19 r.referrer, r.voiceInteractor, window, r.configCallback); 20 } 21 22 ... 23}
在這個方法中,通過newActivity
這個方法(反射)來生成了一個Activity
,生成好了Activity
之後就調用Activity
中的attach
方法,來看一下這個方法裏面幹了些什麼:
1final void attach(Context context, ActivityThread aThread, 2 Instrumentation instr, IBinder token, int ident, 3 Application application, Intent intent, ActivityInfo info, 4 CharSequence title, Activity parent, String id, 5 NonConfigurationInstances lastNonConfigurationInstances, 6 Configuration config, String referrer, IVoiceInteractor voiceInteractor, 7 Window window, ActivityConfigCallback activityConfigCallback) { 8 9 mWindow = new PhoneWindow(this, window, activityConfigCallback); 10 mWindow.setWindowControllerCallback(this); 11 mWindow.setCallback(this); 12 mWindow.setOnWindowDismissedCallback(this); 13 mWindow.getLayoutInflater().setPrivateFactory(this); 14 if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { 15 mWindow.setSoftInputMode(info.softInputMode); 16 } 17 if (info.uiOptions != 0) { 18 mWindow.setUiOptions(info.uiOptions); 19 } 20}
果然,在Activity
的attach
方法中創建了一個Window
,這個Window
就是我們經常聽到的PhoneWindow
View的創建過程
我們大膽的猜測一下,View
應該是被添加到Window
中的,那麼我們來看一下,到底是怎樣添加的呢?上面說到在handlerLauncher
中調用了performLaunchActivity
方法,源碼中還調用了handleResumeActivity
方法,這個方法是在生命週期onCreate
之後,onResume
之前調用的,我們來看一下在這個方法中幹了些什麼:
1final void handleResumeActivity(IBinder token, 2 boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { 3 4 ... 5 r = performResumeActivity(token, clearHide, reason); 6 ... 7 if (r.window == null && !a.mFinished && willBeVisible) { 8 r.window = r.activity.getWindow(); 9 View decor = r.window.getDecorView(); 10 decor.setVisibility(View.INVISIBLE); 11 ViewManager wm = a.getWindowManager(); 12 WindowManager.LayoutParams l = r.window.getAttributes(); 13 a.mDecor = decor; 14 15 ... 16 wm.addView(decor, l); 17 ... 18 } 19}
這裏會先獲取一個Window
和DecorView
,然後拿到ViewManager
(WindowManager
的父類),然後調用addView
方法,ViewManager
和WindowManager
都是接口,那麼我們只要到他的實現類WindowManagerImpl
中去找addView
方法就可以了:
1 @Override 2 public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { 3 applyDefaultToken(params); 4 mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); 5 }
這個mGlobal
就是我們之前的WindowManagerGlobal
,看到這裏相信大家應該有點眉目了吧,最終是由這貨負責把DecorView
添加到Window中,在WindowManagerGlobal
中的addView
方法中還會初始化ViewRootImpl
,有興趣的可以自行看源碼瞭解一下
XML
中的View
是如何添加到DecorView
中的這個也不在這裏分析了,可以參考我之前寫的:https://github.com/24Kshign/Android-Knowledge/blob/a8b38bf4765df13fee3a573249a1dc472f3a7e6c/Android%E6%BA%90%E7%A0%81%E7%9B%B8%E5%85%B3/setContentView%E6%BA%90%E7%A0%81.md
總結
啥也不說了,上圖