作了一段的時間的 android 咱們就開始聽到有人說 AMS、WMS、Window、WindowManager、WindowManagerService等等這些詞彙,可能瞭解可是腦海裏未必有架構圖, 此次咱們就從源碼的角度來了解一下。在閱讀本文以前但願你能夠花點時間瞭解下面幾篇文章:java
1. 插件式換膚框架搭建 - setContentView源碼閱讀 2. Android進程間的通訊 - IPC(機制)Binder的原理和源碼閱讀 2. Android插件化架構 - Activity的啓動流程分析 3. 源碼解析 - View的繪製流程android
####如何閱讀源碼 上面幾篇文章和我今天的這篇文章有着千絲萬縷的聯繫,我爲何能夠把他們分開來?有很重要的一點,咱們是須要用到纔會去看源碼,打個比方我想作一個皮膚切換的功能,那麼我確定須要去了解佈局資源的加載過程,而這個你僅僅從網上看看別人寫好的文章或者 copy 幾行代碼相信你應該很難作到,固然去 github 上面選個 demo 用用也行,但你內心不以爲少了點什麼嗎?還有咱們最好可以點到即止,帶着疑問,只須要清楚咱們想要了解什麼,今天的不少源碼或多或少都會涉及到上面幾篇文章的知識點但咱們不用管。相信我,只要你可以多對着別人的文章本身打開源碼看看,隨着時間的推移當咱們再看系統源碼的時候就會駕輕就熟,解決問題不再是靠蒙和試**(若是你在開發過程當中有時候靠蒙能夠在文章末尾刷個贊),並且若是心中有整個 android 應用層的源碼架構圖,對於開發和解決問題方面屢試不爽(若是你能看懂 native 層的源碼更好)**。git
####咱們的疑問 咱們只是知道 setContentView 方法能夠設置顯示咱們的佈局,這篇 插件式換膚框架搭建 - setContentView源碼閱讀 文章只是帶你們瞭解了佈局的層次結構,但你瞭解 Window 和 WindowManager 都幹了些什麼嗎?又或者當咱們觸摸一個 EditText 的時候會彈出一個系統的鍵盤,爲何彈出鍵盤咱們的 Activity 佈局會自動作調整?咱們彈一個 Toast 但就算咱們退出 Activity 或是整個應用 Toast 仍是會存在?github
咱們以 Activity 的 setContentView 做爲入口能夠看到這麼兩行代碼:windows
/** * Set the activity content from a layout resource. The resource will be * inflated, adding all top-level views to the activity. * * @param layoutResID Resource ID to be inflated. * * @see #setContentView(android.view.View) * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) */ public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); } 複製代碼
getWindow().setContentView(layoutResID) 這行代碼只是去解析咱們的佈局,沒幹其餘任何事情,這行代碼我就再也不分析源碼了,今天的重點不在這裏若是想了解請看這篇插件式換膚框架搭建 - setContentView源碼閱讀,getWindow() 返回的是 mWindow 而 mWindow 是在 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) {
// 建立一個 PhoneWindow 實例
mWindow = new PhoneWindow(this, window);
// 設置一個 ControllerCallback
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
// 這個你看方法名就知道是什麼意思了
mWindow.setOnWindowDismissedCallback(this);
// 給 Window 設置一個 WindowManager
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
// Activity 本身也有一個 WindowManager
mWindowManager = mWindow.getWindowManager();
}
複製代碼
那麼 attach 方法究竟是在何時調用的呢?在 ActivityThread 中的 performLaunchActivity 方法中調用的,這裏涉及到 Activity 的啓動流程分析想了解請看這篇Android插件化架構 - Activity的啓動流程分析bash
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { Activity activity = null; try { // 利用反射建立 Activity 實例對象 java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e); } } try { 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); // 設置主題 int theme = r.activityInfo.getThemeResource(); if (theme != 0) { activity.setTheme(theme); } } } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to start activity " + component + ": " + e.toString(), e); } } // 返回 Activity 實例 return activity; } 複製代碼
目前咱們只知道了經過 setContentView 方法會調用 mWindow 的 setContentView 方法,這個方法只是去解析咱們的佈局而已什麼時都沒作,而 mWindow 的實例實在 activity 的 attach 方法中調用的,而這個方法是由 ActivityThread 調用的而後就沒有了,那麼佈局究竟是怎麼顯示的?markdown
在Android插件化架構 - Activity的啓動流程分析中咱們可以在 ActivityThread 中找到 handleResumeActivity 這個方法:網絡
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { // 先執行 Activity 的 onResume 生命週期方法 // TODO Push resumeArgs into the activity for consideration r = performResumeActivity(token, clearHide, reason); if (r != null) { final Activity a = r.activity; if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); // 獲取 Activity 也就是 PhoneWindow 的根佈局 View decor = r.window.getDecorView(); // 設置爲顯示 decor.setVisibility(View.INVISIBLE); // 獲取 Activity 的 WindowManager 這個對象在上面的 Activity 的 attach 方法中賦值的 ViewManager wm = a.getWindowManager(); // 獲取PhoneWindow的 WindowManager.LayoutParams WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; // 指定 type ,這是一個比較重要的概念,待會下面會介紹到 指定窗口的類型 l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient && !a.mWindowAdded) { a.mWindowAdded = true; // 調用 ViewManager 的 addView 方法 wm.addView(decor, l); } } } else { // If an exception was thrown when trying to resume, then // just end this activity. try { ActivityManagerNative.getDefault() .finishActivity(token, Activity.RESULT_CANCELED, null, Activity.DONT_FINISH_TASK_WITH_ACTIVITY); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } } 複製代碼
最終調用了 ViewManager 的 addView 方法,可是咱們發現 ViewManager 實際上是個接口,因此咱們得去找實現方法,是在上面 Activity 的 attach 方法經過 context.getSystemService(Context.WINDOW_SERVICE) 獲取的,找到 context 的實例類 ContextImpl 的 getSystemService 方法其實調用了 SystemServiceRegistry.getSystemService 方法:session
/** * Gets a system service from a given context. */ 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 是一個靜態的 HashMap 對象,是經過靜態代碼塊賦值的。這裏其實咱們能夠總結一下,經過 Context 獲取的系統服務其實早就被註冊和添加到 SYSTEM_SERVICE_FETCHERS 集合中了,這是典型的單例設計模式。至此咱們總算找到了這個類 WindowManagerImpl 的 addView 方法。
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
複製代碼
WindowManagerImpl 是 ViewManager 的實現類卻把活交給了 WindowManagerGlobal 方法,並且咱們發現 mGlobal 對象盡然是個單例,爲何要這麼設計在文章中我就不作過多的講解了。咱們看下 mGlobal 的 addView 方法:
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { // 一些參數的校驗 // ...... final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; if (parentWindow != null) { // 若是是子 View 須要調整一些佈局的參數 parentWindow.adjustLayoutParamsForSubWindow(wparams); } else { // If there's no parent, then hardware acceleration for this view is // set from the application's hardware acceleration setting. final Context context = view.getContext(); if (context != null && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) { wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; } } ViewRootImpl root; View panelParentView = null; synchronized (mLock) { // 開始觀看系統屬性的變化。 if (mSystemPropertyUpdater == null) { mSystemPropertyUpdater = new Runnable() { @Override public void run() { synchronized (mLock) { for (int i = mRoots.size() - 1; i >= 0; --i) { mRoots.get(i).loadSystemProperties(); } } } }; SystemProperties.addChangeCallback(mSystemPropertyUpdater); } // 經過View 去集合中查找位置,第一次添加確定是 0 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. } // 若是是一個子窗口,咱們去找到它的父窗口的 View // 窗口的類型,下面會介紹到 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { final int count = mViews.size(); for (int i = 0; i < count; i++) { if (mRoots.get(i).mWindow.asBinder() == wparams.token) { panelParentView = mViews.get(i); } } } // 構建ViewRootImpl,暫時無論ViewRootImpl是什麼東西 root = new ViewRootImpl(view.getContext(), display); // 給 View 設置 LayoutParams view.setLayoutParams(wparams); // 下面是一些保存工做添加到集合,一一對應起來 mViews.add(view); mRoots.add(root); mParams.add(wparams); } // 最後一行代碼,用來觸發作事 // 將View顯示到手機上。setView方法比較複雜,走了這麼就最重要的方法在這裏面。 try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. synchronized (mLock) { final int index = findViewLocked(view, false); if (index >= 0) { removeViewLocked(index, true); } } throw e; } } 複製代碼
走了這麼久最重要的方法其實就是 root.setView(view, wparams, panelParentView); 這行代碼,很複雜恰恰這個方法又是最主要的,但願我講得並不太深刻並且通俗易懂。在分析這個以前咱們先講一下上面反覆出現的 type 屬性。
Window(窗口)是有類型的,並且上面咱們也看到了不一樣的 type 會作不一樣的處理,Window 分爲三種類型:系統 Window,應用程序 Window,子 Window
常見的系統Window,好比在手機電量低的時候,會有一個提示電量低的Window,咱們輸入文字的時候,會彈出輸入法Window,還有搜索條Window,來電顯示Window,Toast對應的Window,能夠總結出來,系統Window是獨立與咱們的應用程序的,對於應用程序而言,咱們理論上是沒法建立系統Window,由於沒有權限,這個權限只有系統進程有。所對應的層級區間是 2000 以上
應用程序Window,好比 Activity 就是一個應用程序 Window 從上面源碼能夠看到 type 給的是 TYPE_BASE_APPLICATION 。所對應的層級區間是 1 - 99
子Window,所謂的子Window,是說這個Window必需要有一個父窗體,好比PopWindow,Dialog 等等 。所對應的層級區間是 1000 - 1999
那麼每一個層級具體有那些,請看 WindowManager.LayoutParams中的 type 值,這裏我貼出來可是不作翻譯,由於我英語很差:
/** * Start of window types that represent normal application windows. */ public static final int FIRST_APPLICATION_WINDOW = 1; /** * Window type: an application window that serves as the "base" window * of the overall application; all other application windows will * appear on top of it. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_BASE_APPLICATION = 1; /** * Window type: a normal application window. The {@link #token} must be * an Activity token identifying who the window belongs to. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_APPLICATION = 2; /** * Window type: special application window that is displayed while the * application is starting. Not for use by applications themselves; * this is used by the system to display something until the * application can show its own windows. * In multiuser systems shows on all users' windows. */ public static final int TYPE_APPLICATION_STARTING = 3; /** * Window type: a variation on TYPE_APPLICATION that ensures the window * manager will wait for this window to be drawn before the app is shown. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_DRAWN_APPLICATION = 4; /** * End of types of application windows. */ public static final int LAST_APPLICATION_WINDOW = 99; /** * Start of types of sub-windows. The {@link #token} of these windows * must be set to the window they are attached to. These types of * windows are kept next to their attached window in Z-order, and their * coordinate space is relative to their attached window. */ public static final int FIRST_SUB_WINDOW = 1000; /** * Window type: a panel on top of an application window. These windows * appear on top of their attached window. */ public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW; /** * Window type: window for showing media (such as video). These windows * are displayed behind their attached window. */ public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1; /** * Window type: a sub-panel on top of an application window. These * windows are displayed on top their attached window and any * {@link #TYPE_APPLICATION_PANEL} panels. */ public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2; /** Window type: like {@link #TYPE_APPLICATION_PANEL}, but layout * of the window happens as that of a top-level window, <em>not</em> * as a child of its container. */ public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3; /** * Window type: window for showing overlays on top of media windows. * These windows are displayed between TYPE_APPLICATION_MEDIA and the * application window. They should be translucent to be useful. This * is a big ugly hack so: * @hide */ public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4; /** * Window type: a above sub-panel on top of an application window and it's * sub-panel windows. These windows are displayed on top of their attached window * and any {@link #TYPE_APPLICATION_SUB_PANEL} panels. * @hide */ public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5; /** * End of types of sub-windows. */ public static final int LAST_SUB_WINDOW = 1999; /** * Start of system-specific window types. These are not normally * created by applications. */ public static final int FIRST_SYSTEM_WINDOW = 2000; /** * Window type: the status bar. There can be only one status bar * window; it is placed at the top of the screen, and all other * windows are shifted down so they are below it. * In multiuser systems shows on all users' windows. */ public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW; /** * Window type: the search bar. There can be only one search bar * window; it is placed at the top of the screen. * In multiuser systems shows on all users' windows. */ public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1; /** * Window type: phone. These are non-application windows providing * user interaction with the phone (in particular incoming calls). * These windows are normally placed above all applications, but behind * the status bar. * In multiuser systems shows on all users' windows. */ public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2; /** * Window type: system window, such as low power alert. These windows * are always on top of application windows. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3; /** * Window type: keyguard window. * In multiuser systems shows on all users' windows. * @removed */ public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4; /** * Window type: transient notifications. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5; /** * Window type: system overlay windows, which need to be displayed * on top of everything else. These windows must not take input * focus, or they will interfere with the keyguard. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6; /** * Window type: priority phone UI, which needs to be displayed even if * the keyguard is active. These windows must not take input * focus, or they will interfere with the keyguard. * In multiuser systems shows on all users' windows. */ public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7; /** * Window type: panel that slides out from the status bar * In multiuser systems shows on all users' windows. */ public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8; /** * Window type: dialogs that the keyguard shows * In multiuser systems shows on all users' windows. */ public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9; /** * Window type: internal system error windows, appear on top of * everything they can. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10; /** * Window type: internal input methods windows, which appear above * the normal UI. Application windows may be resized or panned to keep * the input focus visible while this window is displayed. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11; /** * Window type: internal input methods dialog windows, which appear above * the current input method window. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12; /** * Window type: wallpaper window, placed behind any window that wants * to sit on top of the wallpaper. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13; /** * Window type: panel that slides out from over the status bar * In multiuser systems shows on all users' windows. */ public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14; /** * Window type: secure system overlay windows, which need to be displayed * on top of everything else. These windows must not take input * focus, or they will interfere with the keyguard. * * This is exactly like {@link #TYPE_SYSTEM_OVERLAY} except that only the * system itself is allowed to create these overlays. Applications cannot * obtain permission to create secure system overlays. * * In multiuser systems shows only on the owning user's window. * @hide */ public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15; /** * Window type: the drag-and-drop pseudowindow. There is only one * drag layer (at most), and it is placed on top of all other windows. * In multiuser systems shows only on the owning user's window. * @hide */ public static final int TYPE_DRAG = FIRST_SYSTEM_WINDOW+16; /** * Window type: panel that slides out from under the status bar * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17; /** * Window type: (mouse) pointer * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18; /** * Window type: Navigation bar (when distinct from status bar) * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19; /** * Window type: The volume level overlay/dialog shown when the user * changes the system volume. * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20; /** * Window type: The boot progress dialog, goes on top of everything * in the world. * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21; /** * Window type to consume input events when the systemUI bars are hidden. * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22; /** * Window type: Dreams (screen saver) window, just above keyguard. * In multiuser systems shows only on the owning user's window. * @hide */ public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23; /** * Window type: Navigation bar panel (when navigation bar is distinct from status bar) * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24; /** * Window type: Display overlay window. Used to simulate secondary display devices. * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26; /** * Window type: Magnification overlay window. Used to highlight the magnified * portion of a display when accessibility magnification is enabled. * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27; /** * Window type: keyguard scrim window. Shows if keyguard needs to be restarted. * In multiuser systems shows on all users' windows. * @hide */ public static final int TYPE_KEYGUARD_SCRIM = FIRST_SYSTEM_WINDOW+29; /** * Window type: Window for Presentation on top of private * virtual display. */ public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30; /** * Window type: Windows in the voice interaction layer. * @hide */ public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31; /** * Window type: Windows that are overlaid <em>only</em> by a connected {@link * android.accessibilityservice.AccessibilityService} for interception of * user interactions without changing the windows an accessibility service * can introspect. In particular, an accessibility service can introspect * only windows that a sighted user can interact with which is they can touch * these windows or can type into these windows. For example, if there * is a full screen accessibility overlay that is touchable, the windows * below it will be introspectable by an accessibility service even though * they are covered by a touchable window. */ public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32; /** * Window type: Starting window for voice interaction layer. * @hide */ public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33; /** * Window for displaying a handle used for resizing docked stacks. This window is owned * by the system process. * @hide */ public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34; /** * Window type: like {@link #TYPE_APPLICATION_ATTACHED_DIALOG}, but used * by Quick Settings Tiles. * @hide */ public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35; /** * Window type: shares similar characteristics with {@link #TYPE_DREAM}. The layer is * reserved for screenshot region selection. These windows must not take input focus. * @hide */ public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36; /** * End of types of system windows. */ public static final int LAST_SYSTEM_WINDOW = 2999; 複製代碼
回到 ViewRootImpl 中的 setView 方法:
/** * We have one child */ public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; // ...... // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system. // 佈局測量繪製是從這個方法開始的 requestLayout(); try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); } catch (RemoteException e) { mAdded = false; mView = null; mAttachInfo.mRootView = null; mInputChannel = null; mFallbackEventHandler.setView(null); unscheduleTraversals(); setAccessibilityFocus(null, null); throw new RuntimeException("Adding window failed", e); } finally { if (restore) { attrs.restore(); } } // ...... } } 複製代碼
requestLayout() 這個方法頗有含金量,若是想了解看下這篇 源碼解析 - View的繪製流程 ,咱們主要仍是分析mWindowSession.addToDisplay 方法,mWindowSession 又是什麼呢?看到 Session 其實仍是知道什麼意思,是否是真的和網絡的 Session 差很少呢?mWindowSession 是經過 WindowManagerGlobal.getWindowSession(); 獲取的,咱們去看下:
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 IWindowManager getWindowManagerService() { synchronized (WindowManagerGlobal.class) { if (sWindowManagerService == null) { sWindowManagerService = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); try { sWindowManagerService = getWindowManagerService(); ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return sWindowManagerService; } } 複製代碼
這又是一個典型的 IPC 的通訊機制,和 AMS 很是的相似想了解看下這篇 Android進程間的通訊 - IPC(機制)Binder的原理和源碼閱讀 ,如今咱們去服務端 WindowManagerService 的 openSession 方法:
@Override public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client, IInputContext inputContext) { if (client == null) throw new IllegalArgumentException("null client"); if (inputContext == null) throw new IllegalArgumentException("null inputContext"); Session session = new Session(this, callback, client, inputContext); return session; } 複製代碼
Session咱們總算是獲取到了,服務端 WindowManagerService 建立了一個 Session 對象返回回來了,接下來咱們發現 Session 的 addToDisplay 方法來到了 WindowManager 的 addWindow 方法,相信應該差很少快完了吧,這不是人乾的事:
public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) { ...... WindowState attachedWindow = null; final int type = attrs.type; synchronized(mWindowMap) { ...... if (mWindowMap.containsKey(client.asBinder())) { ...... return WindowManagerImpl.ADD_DUPLICATE_ADD; } 複製代碼
這段代碼首先在WindowManagerService類的成員變量mWindowMap所描述的一個HashMap中檢查是否存在一個與參數client所對應的WindowState對象,若是已經存在,那麼就說明WindowManagerService服務已經爲它建立過一個WindowState對象了,所以,這裏就不會繼續往前執行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_DUPLICATE_ADD。咱們繼續往前看代碼:
if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) { attachedWindow = windowForClientLocked(null, attrs.token, false); if (attachedWindow == null) { ...... return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN; } if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) { ...... return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN; } } 複製代碼
參數attrs指向的是一個WindowManager.LayoutParams對象,用來描述正在啓動的Activity組件的UI佈局,當它的成員變量type的值大於等於FIRST_SUB_WINDOW而且小於等於LAST_SUB_WINDOW的時候,就說明如今要增長的是一個子窗口。在這種狀況下,就必需要指定一個父窗口,而這個父窗口是經過數attrs指向的是一個WindowManager.LayoutParams對象的成員變量token來指定的,所以,這段代碼就會調用WindowManagerService類的另一個成員函數windowForClientLocked來得到用來描述這個父窗口的一個WindowState對象,而且保存在變量attachedWindow。
若是獲得變量attachedWindow的值等於null,那麼就說明父窗口不存在,這是不容許的,所以,函數就不會繼續向前執行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN。另外一方面,若是變量attachedWindow的值不等於null,可是它的成員變量mAttrs所指向的一個WindowManager.LayoutParams對象的成員變量type的值也是大於等於FIRST_SUB_WINDOW而且小於等於LAST_SUB_WINDOW,那麼也說明找到的父窗口也是一個子窗口,這種狀況也是不容許的,所以,函數就不會繼續向前執行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN。
繼續看代碼:
// 一些檢查合法的代碼 win = new WindowState(session, client, token, attachedWindow, attrs, viewVisibility); ...... mPolicy.adjustWindowParamsLw(win.mAttrs); res = mPolicy.prepareAddWindowLw(win, attrs); if (res != WindowManagerImpl.ADD_OKAY) { return res; } 複製代碼
經過上面的合法性檢查以後,這裏就能夠爲正在增長的窗口建立一個WindowState對象了。 WindowManagerService類的成員變量mPolicy指向的是一個實現了WindowManagerPolicy接口的窗口管理策略器。在Phone平臺中,這個窗口管理策略器是由com.android.internal.policy.impl.PhoneWindowManager來實現的,它負責對系統中的窗口實現制定一些規則。這裏主要是調用窗口管理策略器的成員函數adjustWindowParamsLw來調整當前正在增長的窗口的佈局參數,以及調用成員函數prepareAddWindowLw來檢查當前應用程序進程請求增長的窗口是不是合法的。若是不是合法的,即變量res的值不等於WindowManagerImpl.ADD_OKAY,那麼函數就不會繼續向前執行,而直接返回錯誤碼res。
咱們就先看到這裏了,你甚至還能夠在去了解 WindowState 的成員變量都有一些啥做用,又或者是那些合法的檢查代碼都有什麼用等等,最後我再畫一張草圖:
我花了大概半個多月的時間才勉強看懂一些源碼,固然包括這篇文章中的全部源碼連接,若是看不太懂須要多花些時間,若是文字讓你受不了能夠看看個人直播視頻,同時這也是自定義View部分的最後一篇文章了。全部分享大綱:Android進階之旅 - 自定義View篇
視頻講解地址:http://pan.baidu.com/s/1pLNXkxl