該文章是一個系列文章,是本人在Android開發的漫漫長途上的一點感想和記錄,我會盡可能按照先易後難的順序進行編寫該系列。該系列引用了《Android開發藝術探索》以及《深刻理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相關知識,另外也借鑑了其餘的優質博客,在此向各位大神表示感謝,膜拜!!!另外,本系列文章知識可能須要有必定Android開發基礎和項目經驗的同窗才能更好理解,也就是說該系列文章面向的是Android中高級開發工程師。java
第四篇了,,接着上一篇說 (怎麼感受仍是沒人評論呢)android
在上一篇文章中咱們主要分析了android.app.ActivityThread的main函數以及setContentView。另外咱們還稍微分析了一下咱們本身的源碼,經過WindowManager添加View。咱們知道調用setContentView把咱們本身的xml佈局添加到了DecorView ID爲ID_ANDROID_CONTENT的佈局後,最終仍是會調用WindowManager.addView把DecorView加入PhoneWindow。到這裏呢,咱們把流程梳理一下。仍是上圖:app
相信讀者根據上圖再結合前面所講的內容應該對Activity的建立和顯示有了初步的認識。那麼本章咱們來繼續講Activity的顯示。該注意的是本系列並不意在帶領讀者去看清每一步具體的源碼。在前面的文章中我也不多貼出源碼。本系列文章意在讓讀者對Android系統有個更總體的把握。我所寫的每一章知識都有可能在實際工做中用到。就如前面所講解的Android下的進程問題以及Activity的生命週期以及本章要講解的View的五大過程的基礎ViewRootImpl。而理解View的**五大過程(通常文章裏都是三大過程)**以及View的事件體系是更好的去自定義View的基礎。 本章接着第Ⅲ篇的文章來說解View框架樹的動力所在ViewRootImpl框架
上一章的最後咱們講到了使用WindowManager添加View。這一章咱們來具體分析。最近我也發表了三篇文章,可反響度通常,我也是剛寫技術博客。確定有些不足之處。此次稍微改變些風格,在分析的時候貼一些源碼上去。ide
從第一篇文章中咱們就知道了使用WindowManager.addView方法添加View。那麼看看該類的實現吧。函數
public interface WindowManager extends ViewManager { //這裏咱們只列出了一部分函數,可是並無addView、updateViewLayout、removeView這三個函數 public Display getDefaultDisplay(); public void removeViewImmediate(View view); ... }
好吧,果真沒有這麼簡單,WindowManager是個接口,並且在其方法中沒有找到addView方法,那麼咱們只能看看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); }
還好是找到了,ViewManager沒有再繼承其餘接口了。(要否則真不知道要找到何時去。) 既然WindowManager是個接口,那確定要找它的實現類了。(在這裏安利一個比較簡單的方法,在Android Studio中) 這裏咱們很幸運只找到了一個WindowManager的實現類(有的時候可能有不少個,當出現多個的時候,那只有一個個去看了)。這裏咱們來看WindowManagerImpl(看到這個 類的名字咱們內心瞭然一笑,果真是java的命名規範)。this
public final class WindowManagerImpl implements WindowManager { private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); private final Context mContext; private final Window mParentWindow; private IBinder mDefaultToken; public WindowManagerImpl(Context context) { this(context, null); } private WindowManagerImpl(Context context, Window parentWindow) { mContext = context; mParentWindow = parentWindow; } public WindowManagerImpl createLocalWindowManager(Window parentWindow) { return new WindowManagerImpl(mContext, parentWindow); } public WindowManagerImpl createPresentationWindowManager(Context displayContext) { return new WindowManagerImpl(displayContext, mParentWindow); } /** * Sets the window token to assign when none is specified by the client or * available from the parent window. * * @param token The default token to assign. */ public void setDefaultToken(IBinder token) { mDefaultToken = token; } @Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); } @Override public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.updateViewLayout(view, params); } private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) { // Only use the default token if we don't have a parent window. if (mDefaultToken != null && mParentWindow == null) { if (!(params instanceof WindowManager.LayoutParams)) { throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); } // Only use the default token if we don't already have a token. final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; if (wparams.token == null) { wparams.token = mDefaultToken; } } } @Override public void removeView(View view) { mGlobal.removeView(view, false); } @Override public void removeViewImmediate(View view) { mGlobal.removeView(view, true); } @Override public void requestAppKeyboardShortcuts( final KeyboardShortcutsReceiver receiver, int deviceId) { IResultReceiver resultReceiver = new IResultReceiver.Stub() { @Override public void send(int resultCode, Bundle resultData) throws RemoteException { List<KeyboardShortcutGroup> result = resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY); receiver.onKeyboardShortcutsReceived(result); } }; try { WindowManagerGlobal.getWindowManagerService() .requestAppKeyboardShortcuts(resultReceiver, deviceId); } catch (RemoteException e) { } } @Override public Display getDefaultDisplay() { return mContext.getDisplay(); } }
WindowManagerImpl的源碼如上所示,咱們能夠看到WindowManagerImpl的addView方法,WindowManagerImpl把工做交給了WindowManagerGlobalcode
/** WindowManagerGlobal 源碼比較長,這裏咱們只列出了一部分 */ public final class WindowManagerGlobal { private WindowManagerGlobal() { } public static void initialize() { getWindowManagerService(); } public static WindowManagerGlobal getInstance() { synchronized (WindowManagerGlobal.class) { if (sDefaultWindowManager == null) { sDefaultWindowManager = new WindowManagerGlobal(); } return sDefaultWindowManager; } } public static IWindowManager getWindowManagerService() { synchronized (WindowManagerGlobal.class) { if (sWindowManagerService == null) { sWindowManagerService = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); try { if (sWindowManagerService != null) { ValueAnimator.setDurationScale( sWindowManagerService.getCurrentAnimatorScale()); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return sWindowManagerService; } } public static IWindowSession getWindowSession() { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { InputMethodManager imm = InputMethodManager.getInstance(); IWindowManager windowManager = getWindowManagerService(); sWindowSession = windowManager.openSession( new IWindowSessionCallback.Stub() { @Override public void onAnimatorScaleChanged(float scale) { ValueAnimator.setDurationScale(scale); } }, imm.getClient(), imm.getInputContext()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return sWindowSession; } } public static IWindowSession peekWindowSession() { synchronized (WindowManagerGlobal.class) { return sWindowSession; } } //addView方法 public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ... //參數檢查 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; if (parentWindow != null) { //① 若是當前窗口須要被添加爲另外一個窗口的附屬窗口(子窗口),則須要父窗口視本身的狀況對當前窗口的佈局參數進行調整 parentWindow.adjustLayoutParamsForSubWindow(wparams); } ViewRootImpl root; View panelParentView = null; int index = findViewLocked(view, false); if (index >= 0) { if (mDyingViews.contains(view)) { mRoots.get(index).doDie(); } else { //同一個View不容許被添加2次 throw new IllegalStateException("View " + view + " has already been added to the window manager."); } } //② 建立一個ViewRootImpl對象並保存在root變量中 root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); //③ 保存做爲窗口的控件、佈局參數以及新建的ViewRootImpl mViews.add(view); mRoots.add(root); mParams.add(wparams); // do this last because it fires off messages to start doing things try { // ④ 將做爲窗口的控件設置給ViewRootImpl.這個動做將致使ViewRootImpl向WMS添加新的窗口、申請Surface以及託管控件在Surface上的重繪工做。這纔是真正意義上完成了窗口的添加工做。 root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. if (index >= 0) { removeViewLocked(index, true); } throw e; } } } public void updateViewLayout(View view, ViewGroup.LayoutParams params) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } if (!(params instanceof WindowManager.LayoutParams)) { throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); } final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; view.setLayoutParams(wparams); synchronized (mLock) { int index = findViewLocked(view, true); ViewRootImpl root = mRoots.get(index); mParams.remove(index); mParams.add(index, wparams); root.setLayoutParams(wparams, false); } } public void removeView(View view, boolean immediate) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } synchronized (mLock) { 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); } } }
咱們能夠看到WindowManagerGlobal的私有構造函數以及getInstance()這個熟悉的靜態方法名字。能夠看出WindowManagerGlobal是個典型的單例。 WindowManagerGlobal 的addView方法並不複雜,其主要的關鍵點咱們已經標註並寫了註釋。也就是說WindowManagerGlobal的職責以下:xml
本篇總結 本篇文章分析了WindowManager的addView的過程,WindowManager是個接口,它的實現類是WindowManagerImpl類,而WindowManagerImpl又把相關邏輯交給了WindowManagerGlobal處理。WindowManagerGlobal是個單例類,它在進程中只存在一個實例,是它內部的addView方法最終建立了咱們的核心類ViewRootImpl。ViewRootImpl實現了ViewParent接口,做爲整個控件樹的根部,它是控件樹正常運做的動力所在,控件的測量、佈局、繪製以及輸入事件的派發處理竇世友ViewRootImpl出發。它是WindowManagerGlobal的實際工做者。
下篇預告 在下一篇文章中咱們將深刻介紹ViewRootImpl的工做流程。測量、佈局、以及繪製。
此致,敬禮