從原始碼的角度淺談Activity、Window、View之間的關係
序言:很多人都會用Activity、Window、View,但是你知道他們是怎樣加載出來並呈現在你眼前的嗎?你知道他們之間有著鮮為人知的關係嗎?

image
講個很簡單的例子,這一天天氣甚好,小明外出寫生,小明背了一包東西,畫板啊,紙啊,筆啊什麼的,然後小明找了一處風景甚好的地方,從包裡拿出畫板,紙,筆然後開始畫畫,不一會兒小明就畫完了一幅風景圖。在這個例子當中,畫板就好比 Activity
,紙就好比 Window
,而筆就是 View
,我們所看到的就是這幅畫,是通過筆一點一點畫出來的,在哪裡畫呢?當然是紙上了,而最終承載這幅畫的東西就是畫板了。這麼說可能不太生動,下面,我們從原始碼的角度來看看這三者的關係。
Activity的建立過程
我們都知道,Activity啟動的時候是從ActivityThread中的Handler中發起的,然後經過handlerLauncher等一系列方法,如果還不知道的話可以去參考我之前寫的 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" target="_blank" rel="nofollow,noindex">一步一步帶你探索Activity的啟動流程
ActivityThread類: private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { ... WindowManagerGlobal.initialize(); Activity a = performLaunchActivity(r, customIntent); ... }
在這裡先呼叫了 WindowManagerGlobal
中的初始化方法初始化了 WindowManagerService
,看名字大概就能知道這是一個 WindowManager
的服務,通過這個服務可以對頁面進行操作;然後通過呼叫 performLaunchActivity
方法生成了一個Activity。
Window的建立過程
上面通過 performLaunchActivity
方法生成了一個Activity,我們來看看是怎樣生成的:
ActivityThread類: private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ... Activity activity = null; try { activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent); } catch (Exception e) { ... } ... if (activity != null) { activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback); } ... }
在這個方法中,通過 newActivity
這個方法(反射)來生成了一個 Activity
,生成好了 Activity
之後就呼叫 Activity
中的 attach
方法,來看一下這個方法裡面幹了些什麼:
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window, ActivityConfigCallback activityConfigCallback) { mWindow = new PhoneWindow(this, window, activityConfigCallback); mWindow.setWindowControllerCallback(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); } if (info.uiOptions != 0) { mWindow.setUiOptions(info.uiOptions); } }
果然,在 Activity
的 attach
方法中建立了一個 Window
,這個 Window
就是我們經常聽到的 PhoneWindow
View的建立過程
我們大膽的猜測一下, View
應該是被新增到 Window
中的,那麼我們來看一下,到底是怎樣新增的呢?上面說到在 handlerLauncher
中呼叫了 performLaunchActivity
方法,原始碼中還呼叫了 handleResumeActivity
方法,這個方法是在生命週期 onCreate
之後, onResume
之前呼叫的,我們來看一下在這個方法中幹了些什麼:
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { ... r = performResumeActivity(token, clearHide, reason); ... if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; ... wm.addView(decor, l); ... } }
這裡會先獲取一個 Window
和 DecorView
,然後拿到 ViewManager
( WindowManager
的父類),然後呼叫 addView
方法, ViewManager
和 WindowManager
都是介面,那麼我們只要到他的實現類 WindowManagerImpl
中去找 addView
方法就可以了:
@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); }
這個 mGlobal
就是我們之前的 WindowManagerGlobal
,看到這裡相信大家應該有點眉目了吧,最終是由這貨負責把 DecorView
新增到Window中,在 WindowManagerGlobal
中的 addView
方法中還會初始化 ViewRootImpl
,有興趣的可以自行看原始碼瞭解一下
XML
中的 View
是如何新增到 DecorView
中的這個也不在這裡分析了,可以參考我之前寫的 一步一步帶你解析setContentView原始碼
總結
啥也不說了,上圖

image

公眾號:IT先森養成記