在 Adroid 中 Window 表示一個窗口的概念,任何能展示的視圖都必須是掛載于 Window 上的,例如 Activity,Toast,Dialog,PopupWindow 等控件都有承載展示給用戶的視圖的 Window。而 Window 是一個抽線類,它的直接子類是 PhoneWindow。Window 可以接受事件,例如觸摸事件,鍵盤的響應事件等。在事件傳遞機制就是將事件首先傳遞給 Window,然后再由 Window 傳遞給掛載在 Window 中的 DecorView 中,之后再傳遞到對應的 View 上。除此之后,在 Activity#setContentView(layoutId) 內部也是去調用 PhoneWindow#setContentView(layoutId) 去實現。
getWindow() 就是 Activity 所依賴的 Window對象,而它是在哪里創建呢?查看源碼,可以知道 PhoneWindow 就是在 Activity#attach() 中被調用的。在這個方法主要做的事是:(1)創建 PhoneWindow 對象 ;(2) 設置事件監聽,接受觸摸事件,鍵盤輸入等事件;(3)給 activity 的成員變量賦值。而 attach() 方法它是在 ActivityThread#performLaunchActivity() 方法中調用,這里涉及到 Activity 的生命周期的分析 ,這里不是討論的終重點,先行跳過?,F在呢,我們就知道了 activity 中的 window 在哪里創建的了。
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) { attachBaseContext(context); mFragments.attachHost(null /*parent*/); mWindow = new PhoneWindow(this);//(1)創建 PhoneWindow 對象 mWindow.setCallback(this);// (2)設置事件監聽器 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); } // (3)activity 成員變量的賦值 mUiThread = Thread.currentThread(); mMainThread = aThread; mInstrumentation = instr; mToken = token; mIdent = ident; mApplication = application; mIntent = intent; mReferrer = referrer; mComponent = intent.getComponent(); mActivityInfo = info; mTitle = title; mParent = parent; mEmbeddedID = id; mLastNonConfigurationInstances = lastNonConfigurationInstances; if (voiceInteractor != null) { if (lastNonConfigurationInstances != null) { mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor; } else { mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this, Looper.myLooper()); } } mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); if (mParent != null) { mWindow.setContainer(mParent.getWindow()); } mWindowManager = mWindow.getWindowManager(); mCurrentConfig = config; }在瀏覽源碼之前,需要了解幾個概念
DecorView 就是 Activity 的根視圖,它是一個 FrameLayout 視圖。有標題欄和內容欄,不過不同版本可能有差異。mParentContent 就是 DecorView 的直接子 View,它是 Decor 的內容欄。它有指定的 id :android.R.id.content,通過 setContentView(layoutId) 的 layoutId 對應的 View 就是掛載在 mContentParent 上的。初次調用時先檢測 mContentParent 是否為 null,當該值為 null 時,就去 generateDecor() 裝載一個根視圖 Decor。然后調用 mContentParent.addView(view, params) 往 mContentParent 掛載 setContentView(layoutId) 對應的 View。
@Overridepublic void setContentView(View view, ViewGroup.LayoutParams params) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens. if (mContentParent == null) {//mContentParent 就是 DecorView 中的一級子View installDecor();//裝載一個 Activity 的根視圖 } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { view.setLayoutParams(params); final Scene newScene = new Scene(mContentParent, view); transitionTo(newScene); } else { mContentParent.addView(view, params);// 往 mContentParent 掛載 setContentView(layoutId) 對應的 View。 } mContentParent.requestApplyInsets();}private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); // 創建 Activity 的根視圖 ... } if (mContentParent == null) { mContentParent = generateLayout(mDecor); //給 mContentParent 賦值 ... }}//將內容欄添加到 Decor 中protected ViewGroup generateLayout(DecorView decor) { ... View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); ...}當 Decor 初始化完畢之后,會通過在 Callback#onContentChanged(),表示 Decor 已經完成初始化操作.這個 Callback 就是在 Activity#attach() 方法設置的,因為在 Activity 中實現了 Callback 接口,因此該方法會回調到 Activity 中的 onContentChanged()方法中,不過這個方法是空實現,用戶可已重寫該方法。
final Callback cb = getCallback();// 在 DecorView 初始化完畢之后的回調 onContentChanged()方法。if (cb != null && !isDestroyed()) { cb.onContentChanged();}現在 Window 創建好了,對應的 DecorView 也初始完畢了,接下來就是發揮 WindowManager 的作用了,那就是往 Window 中添加 DecorView了。在這個過程涉及到 Activity 的生命周期的回調,我們這里暫且跳過,直接切入 Window 添加 DecorView 的源碼。
Activity 的生命周期的回調是一個 ipC 過程,在 ActivityThread#handleResumeActivity() 就是負責 Activity#onResume() 生命周期的回調。在 handleResumeActivity 方法內部調用 wm.addView(decor, l); 將 DecorView 添加到 Activity 對應的 Window 中。這個過程實際上是有 WindowManager 去完成的,每一 Window 都會對應一個 WindowManager,通過 WindowManager 去往 Window 中 addView,removeView或者是 updateViewLayout 操作。
//#handleResumeActivity 代碼片段final Activity a = r.activity;if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE);//設置 DecorView 不可見 ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; wm.addView(decor, l);//往 Window 添加 View }}WindowManager 是一個接口,因此 addView 的操作是有其子 View 去實現的,那么它的子 View 是誰呢?下面的代碼可以看到 WindowManager 的實現類就是 WindowManagerImpl。
public final class WindowManagerImpl implements WindowManager {}WindowManagerImpl#addView 的操作都是交給 WindowManagerGlobal 的類去執行。
@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mDisplay, mParentWindow); }WindowManagerGlobal 是單例的方式提供對象的,內部維護了 mView,mRoots,mParams,mDyingViews 等集合。
mViews 保存的就是添加到 Window 中的 View 對象。mRoots 保存的就是 每一個 Window 對應的 ViewRootImpl 對象。mDyingViews 保存的是哪些調用了 removeView 但是刪除操作還未完成的 Window 對象對應的 View。mParams 保存的是每一個 Window 的布局參數。// 懶漢式單例模式public static WindowManagerGlobal getInstance() { synchronized (WindowManagerGlobal.class) { if (sDefaultWindowManager == null) { sDefaultWindowManager = new WindowManagerGlobal(); } return sDefaultWindowManager; }}在 addView 中創建 Window 對應的 ViewRootImpl 對象。并且往mViews,mRoots,mParams,mDyingViews 中保存當前 View 的信息。 最后調用 root.setView(view, wparams, panelParentView);
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { //校驗操作 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; ViewRootImpl root; View panelParentView = null; synchronized (mLock) { int index = findViewLocked(view, false); if (index >= 0) { if (mDyingViews.contains(view)) { // Don't wait for MSG_DIE to make it's way through root's queue. mRoots.get(index).doDie(); } else { throw new IllegalStateException("View " + view + " has already been added to the window manager."); } // The previous removeView() had not completed executing. Now it has. } //創建 ViewRootImpl 對象。 root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); //保存到集合中 mViews.add(view); mRoots.add(root); mParams.add(wparams); } // 調用 View 的繪制代碼 root.setView(view, wparams, panelParentView);}現在 Window 對應的視圖已經準備好了,但是該仍然是不可見的,接下來繼續看ActivityThread#handleResumeActivity 代碼。這時會去調用 Activity#makeVisible()方法。因為在 1.4.1、 ActivityThread#handleResumeActivity 中 DecorView 被設置為 invisible 因此在這時Decor 會被設置為 View.VISIBLE。至此 View 就真正的顯示出來了。
//ActivityThread#handleResumeActivityr.activity.mVisibleFromServer = true;if (r.activity.mVisibleFromClient) { r.activity.makeVisible();}//Activity#makeVisible()void makeVisible() { if (!mWindowAdded) {//判斷 DecorView 是否被添加到 Window 中 ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE);//讓 View 設置為可見}新聞熱點
疑難解答