Android進階(四)Window和WindowManager
- Window可以理解為窗體,是一種抽象概念,其中具體表現形式就是View。
- WindowManager是用來新增、刪除、更新Window(具體是View)的。
- WindowManagerService是在SystemServer程序中的,WindowManger中的所有操作都是由WMS來最終完成的。
三者關係:

2、Window
一個抽象類,其具體實現為PhoneWindow
3、WindowManager
WindowManager繼承了ViewManager,ViewManager中定義了添、刪、改的方法。
public interface ViewManager { public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view); } 複製程式碼
4、Activity中PhoneWindow的建立
在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) { attachBaseContext(context); mFragments.attachHost(null /*parent*/); //實現了PhoneWindow的建立 mWindow = new PhoneWindow(this, window, activityConfigCallback); mWindow.setWindowControllerCallback(this); //設定回撥 mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); ...... //(1)設定WindowManager 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; mWindow.setColorMode(info.colorMode); } 複製程式碼
(1)context.getSystemService(Context.WINDOW_SERVICE)獲取的是什麼?
Context具體實現在ContextImpl中
#ContextImpl @Override public Object getSystemService(String name) { return SystemServiceRegistry.getSystemService(this, name); } #SystemServiceRegistry public static Object getSystemService(ContextImpl ctx, String name) { ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); return fetcher != null ? fetcher.getService(ctx) : null; } 複製程式碼
SYSTEM_SERVICE_FETCHERS是一個map,key為WINDOW_SERVICE時,對應的value是什麼?
#SystemServiceRegistry registerService(Context.WINDOW_SERVICE, WindowManager.class, new CachedServiceFetcher<WindowManager>() { @Override public WindowManager createService(ContextImpl ctx) { return new WindowManagerImpl(ctx); }}); 複製程式碼
註冊服務時,傳入Context.WINDOW_SERVICE時,最終返回的是WindowManagerImpl。
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) { ...... if (wm == null) { wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); } mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); } 複製程式碼
通過createLocalWindowManager,最終建立了WindowManagerImpl物件。
二、Activity中Window新增
1、ActivityThread的handleResumeActivity
當介面要與使用者進行互動時,需要呼叫handleResumeActivity方法
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { ...... (1) //最終呼叫Activity的onResume 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; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (r.mPreserveWindow) { a.mWindowAdded = true; r.mPreserveWindow = false; // Normally the ViewRoot sets up callbacks with the Activity // in addView->ViewRootImpl#setView. If we are instead reusing // the decor view we have to notify the view root that the // callbacks may have changed. ViewRootImpl impl = decor.getViewRootImpl(); if (impl != null) { impl.notifyChildRebuilt(); } } if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true; (2) wm.addView(decor, l); } ...... } } 複製程式碼
(2)最終呼叫了WindowManagerImpl的addView方法
2、WindowManager的addView
#WindowManagerImpl @Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); } 複製程式碼
內部呼叫WindowManagerGlobal的addView方法
3、WindowManagerGlobal
(1)部分引數:
//Window所對應的view private final ArrayList<View> mViews = new ArrayList<View>(); //Window所對應的ViewRootImpl private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); //Window所對應的佈局引數 private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>(); //已經呼叫了removeView,但是還沒完成的Window物件 private final ArraySet<View> mDyingViews = new ArraySet<View>(); 複製程式碼
(2)addView方法
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ...... //建立ViewRootImpl root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); //將引數新增到list中 mViews.add(view); mRoots.add(root); mParams.add(wparams); try { //呼叫了ViewRootImpl的setView方法 root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. if (index >= 0) { removeViewLocked(index, true); } throw e; } } } 複製程式碼
呼叫了ViewRootImpl的setView方法
#ViewRootImpl public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { ...... (1) requestLayout(); ...... try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); (2) res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); } ...... } } } 複製程式碼
(1)通過呼叫requestLayout,最終通過performTraversals方法完成View的測量、佈局、繪製操作。
(2)呼叫了IWindowSession的addToDisplay方法,IWindowSession實現是什麼?
IWindowSession是Binder物件,最終呼叫的是SystemServer程序的Session的addToDisplay方法
#Session @Override public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outStableInsets, outOutsets, outInputChannel); } 複製程式碼
呼叫了WMS的addWindow方法,其中使用list將Session進行儲存,併為視窗分配畫布Surface,用來顯示頁面,並確定視窗的顯示次序。
時序圖:

三、WindowManager刪除View
1、WindowManagerGlobal的removeView
WindowManager的removeView方法,最終同樣呼叫了WindowManagerGlobal的removeView方法
#WindowManagerGlobal public void removeView(View view, boolean immediate) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } synchronized (mLock) { //找到view對應的索引位置 int index = findViewLocked(view, true); View curView = mRoots.get(index).getView(); //下一步操作 removeViewLocked(index, immediate); if (curView == view) { return; } throw new IllegalStateException("Calling with view " + view + " but the ViewAncestor is attached to " + curView); } } private void removeViewLocked(int index, boolean immediate) { //獲取到對應的ViewRootImpl ViewRootImpl root = mRoots.get(index); View view = root.getView(); if (view != null) { InputMethodManager imm = InputMethodManager.getInstance(); if (imm != null) { imm.windowDismissed(mViews.get(index).getWindowToken()); } } //進行下一步操作 boolean deferred = root.die(immediate); if (view != null) { view.assignParent(null); if (deferred) { mDyingViews.add(view); } } } 複製程式碼
2、die
boolean die(boolean immediate) { if (immediate && !mIsInTraversal) { //(1)如果immediate為true,立即刪除 doDie(); return false; } if (!mIsDrawing) { destroyHardwareRenderer(); } else { Log.e(mTag, "Attempting to destroy the window while drawing!\n" + "window=" + this + ", title=" + mWindowAttributes.getTitle()); } (2) mHandler.sendEmptyMessage(MSG_DIE); return true; } 複製程式碼
(1)同步刪除直接呼叫doDie方法
(2)非同步刪除通過Handler傳送了MSG_DIE的訊息,沒有真正刪除。只是將view新增到一個待刪除的列表mDyingViews中
void doDie() { ...... if (mAdded) { //(1)下一步刪除操作 dispatchDetachedFromWindow();//5 } ...... //(2) WindowManagerGlobal.getInstance().doRemoveView(this); } 複製程式碼
(1)呼叫了Session的remove方法,最終交給WMS進行處理。
void dispatchDetachedFromWindow() { ... try { mWindowSession.remove(mWindow); } catch (RemoteException e) { } ... } 複製程式碼
(2)將該view對應索引在list中刪除
void doRemoveView(ViewRootImpl root) { synchronized (mLock) { final int index = mRoots.indexOf(root); if (index >= 0) { //將該view對應索引在list中刪除 mRoots.remove(index); mParams.remove(index); final View view = mViews.remove(index); mDyingViews.remove(view); } } if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) { doTrimForeground(); } } 複製程式碼
四、WindowManager更新View
1、WindowManagerGlobal的updateViewLayout
#WindowManagerGlobal public void updateViewLayout(View view, ViewGroup.LayoutParams params) { ...... final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; //更新view的LayoutParams view.setLayoutParams(wparams); synchronized (mLock) { //找到view索引 int index = findViewLocked(view, true); ViewRootImpl root = mRoots.get(index); //移除之前的LayoutParams mParams.remove(index); //新增新的LayoutParams mParams.add(index, wparams); //更新ViewRootImpl,呼叫scheduleTraversals方法 root.setLayoutParams(wparams, false); } } 複製程式碼
2、ViewRootImpl的setLayoutParams
#ViewRootImpl
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) { synchronized (this) { ...... scheduleTraversals(); } } void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); //註冊了TraversalRunnable mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } } final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } void doTraversal() { if (mTraversalScheduled) { ...... performTraversals(); ...... } } private void performTraversals() { ...... relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); ...... } private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { ...... int relayoutResult = mWindowSession.relayout( mWindow, mSeq, params, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingMergedConfiguration, mSurface); ...... return relayoutResult; } 複製程式碼
最終通過Session的relayout方法實現view的更新
參考資料:
- 《Android開發藝術探索》
- 《Android進階解密》