源码阅读分析 - Window底层原理与系统架构
做了一段的時間的 android 我們就開始聽到有人說 AMS、WMS、Window、WindowManager、WindowManagerService等等這些詞匯,可能了解但是腦海里未必有架構(gòu)圖, 這次我們就從源碼的角度來了解一下。在閱讀本文之前希望你可以花點(diǎn)時間了解下面幾篇文章:
1. 插件式換膚框架搭建 - setContentView源碼閱讀
2. Android進(jìn)程間的通信 - IPC(機(jī)制)Binder的原理和源碼閱讀
2. Android插件化架構(gòu) - Activity的啟動流程分析
3. 源碼解析 - View的繪制流程
如何閱讀源碼
上面幾篇文章和我今天的這篇文章有著千絲萬縷的聯(lián)系,我為什么可以把他們分開來?有很重要的一點(diǎn),我們是需要用到才會去看源碼,打個比方我想做一個皮膚切換的功能,那么我肯定需要去了解布局資源的加載過程,而這個你僅僅從網(wǎng)上看看別人寫好的文章或者 copy 幾行代碼相信你應(yīng)該很難做到,當(dāng)然去 github 上面選個 demo 用用也行,但你心里不覺得少了點(diǎn)什么嗎?還有我們最好能夠點(diǎn)到即止,帶著疑問,只需要清楚我們想要了解什么,今天的很多源碼或多或少都會涉及到上面幾篇文章的知識點(diǎn)但我們不用管。相信我,只要你能夠多對著別人的文章自己打開源碼看看,隨著時間的推移當(dāng)我們再看系統(tǒng)源碼的時候就會得心應(yīng)手,解決問題再也不是靠蒙和試(如果你在開發(fā)過程中有時候靠蒙可以在文章末尾刷個贊),而且如果心中有整個 android 應(yīng)用層的源碼架構(gòu)圖,對于開發(fā)和解決問題方面屢試不爽(如果你能看懂 native 層的源碼更好)。
我們的疑問
我們只是知道 setContentView 方法可以設(shè)置顯示我們的布局,這篇 插件式換膚框架搭建 - setContentView源碼閱讀 文章只是帶大家了解了布局的層次結(jié)構(gòu),但你了解 Window 和 WindowManager 都干了些什么嗎?又或者當(dāng)我們觸摸一個 EditText 的時候會彈出一個系統(tǒng)的鍵盤,為什么彈出鍵盤我們的 Activity 布局會自動做調(diào)整?我們彈一個 Toast 但就算我們退出 Activity 或是整個應(yīng)用 Toast 還是會存在?
PhoneWindow的創(chuàng)建過程
我們以 Activity 的 setContentView 作為入口可以看到這么兩行代碼:
/*** 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) 這行代碼只是去解析我們的布局,沒干其他任何事情,這行代碼我就不再分析源碼了,今天的重點(diǎn)不在這里如果想了解請看這篇插件式換膚框架搭建 - setContentView源碼閱讀,getWindow() 返回的是 mWindow 而 mWindow 是在 attach 方法中實(shí)例化的:
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) {// 創(chuàng)建一個 PhoneWindow 實(shí)例mWindow = new PhoneWindow(this, window);// 設(shè)置一個 ControllerCallbackmWindow.setWindowControllerCallback(this);mWindow.setCallback(this);// 這個你看方法名就知道是什么意思了mWindow.setOnWindowDismissedCallback(this);// 給 Window 設(shè)置一個 WindowManager mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);// Activity 自己也有一個 WindowManagermWindowManager = mWindow.getWindowManager();}那么 attach 方法到底是在什么時候調(diào)用的呢?在 ActivityThread 中的 performLaunchActivity 方法中調(diào)用的,這里涉及到 Activity 的啟動流程分析想了解請看這篇Android插件化架構(gòu) - Activity的啟動流程分析
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {Activity activity = null;try {// 利用反射創(chuàng)建 Activity 實(shí)例對象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);// 設(shè)置主題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 實(shí)例return activity;}目前我們只知道了通過 setContentView 方法會調(diào)用 mWindow 的 setContentView 方法,這個方法只是去解析我們的布局而已什么時都沒做,而 mWindow 的實(shí)例實(shí)在 activity 的 attach 方法中調(diào)用的,而這個方法是由 ActivityThread 調(diào)用的然后就沒有了,那么布局到底是怎么顯示的?
布局的測量和繪制過程
在Android插件化架構(gòu) - Activity的啟動流程分析中我們能夠在 ActivityThread 中找到 handleResumeActivity 這個方法:
final void handleResumeActivity(IBinder token,boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {// 先執(zhí)行 Activity 的 onResume 生命周期方法// TODO Push resumeArgs into the activity for considerationr = 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();// 設(shè)置為顯示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;// 調(diào)用 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();}}}最終調(diào)用了 ViewManager 的 addView 方法,但是我們發(fā)現(xiàn) ViewManager 其實(shí)是個接口,所以我們得去找實(shí)現(xiàn)方法,是在上面 Activity 的 attach 方法通過 context.getSystemService(Context.WINDOW_SERVICE) 獲取的,找到 context 的實(shí)例類 ContextImpl 的 getSystemService 方法其實(shí)調(diào)用了 SystemServiceRegistry.getSystemService 方法:
/*** 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 是一個靜態(tài)的 HashMap 對象,是通過靜態(tài)代碼塊賦值的。這里其實(shí)我們可以總結(jié)一下,通過 Context 獲取的系統(tǒng)服務(wù)其實(shí)早就被注冊和添加到 SYSTEM_SERVICE_FETCHERS 集合中了,這是典型的單例設(shè)計模式。至此我們總算找到了這個類 WindowManagerImpl 的 addView 方法。
@Overridepublic void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {applyDefaultToken(params);mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);}WindowManagerImpl 是 ViewManager 的實(shí)現(xiàn)類卻把活交給了 WindowManagerGlobal 方法,而且我們發(fā)現(xiàn) mGlobal 對象盡然是個單例,為什么要這么設(shè)計在文章中我就不做過多的講解了。我們看下 mGlobal 的 addView 方法:
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {// 一些參數(shù)的校驗// ......final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;if (parentWindow != null) {// 如果是子 View 需要調(diào)整一些布局的參數(shù)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) {// 開始觀看系統(tǒng)屬性的變化。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 去集合中查找位置,第一次添加肯定是 0int 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 {// 如果不是正在銷毀,又添加過那么拋異常結(jié)束運(yùn)行throw new IllegalStateException("View " + view+ " has already been added to the window manager.");}// The previous removeView() had not completed executing. Now it has.}// 如果是一個子窗口,我們?nèi)フ业剿母复翱诘?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);}}}// 構(gòu)建ViewRootImpl,暫時不管ViewRootImpl是什么東西root = new ViewRootImpl(view.getContext(), display);// 給 View 設(shè)置 LayoutParamsview.setLayoutParams(wparams);// 下面是一些保存工作添加到集合,一一對應(yīng)起來mViews.add(view);mRoots.add(root);mParams.add(wparams);}// 最后一行代碼,用來觸發(fā)做事// 將View顯示到手機(jī)上。setView方法比較復(fù)雜,走了這么就最重要的方法在這里面。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;}}走了這么久最重要的方法其實(shí)就是 root.setView(view, wparams, panelParentView); 這行代碼,很復(fù)雜偏偏這個方法又是最主要的,希望我講得并不太深入而且通俗易懂。在分析這個之前我們先講一下上面反復(fù)出現(xiàn)的 type 屬性。
Window的三種類型
Window(窗口)是有類型的,而且上面我們也看到了不同的 type 會做不同的處理,Window 分為三種類型:系統(tǒng) Window,應(yīng)用程序 Window,子 Window
常見的系統(tǒng)Window,比如在手機(jī)電量低的時候,會有一個提示電量低的Window,我們輸入文字的時候,會彈出輸入法Window,還有搜索條Window,來電顯示W(wǎng)indow,Toast對應(yīng)的Window,可以總結(jié)出來,系統(tǒng)Window是獨(dú)立與我們的應(yīng)用程序的,對于應(yīng)用程序而言,我們理論上是無法創(chuàng)建系統(tǒng)Window,因為沒有權(quán)限,這個權(quán)限只有系統(tǒng)進(jìn)程有。所對應(yīng)的層級區(qū)間是 2000 以上
應(yīng)用程序Window,比如 Activity 就是一個應(yīng)用程序 Window 從上面源碼可以看到 type 給的是 TYPE_BASE_APPLICATION 。所對應(yīng)的層級區(qū)間是 1 - 99
子Window,所謂的子Window,是說這個Window必須要有一個父窗體,比如PopWindow,Dialog 等等 。所對應(yīng)的層級區(qū)間是 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;窗口與WindowManagerService服務(wù)的連接過程
回到 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 其實(shí)還是知道什么意思,是不是真的和網(wǎng)絡(luò)的 Session 差不多呢?mWindowSession 是通過 WindowManagerGlobal.getWindowSession(); 獲取的,我們?nèi)タ聪?#xff1a;
public static IWindowSession getWindowSession() {synchronized (WindowManagerGlobal.class) {if (sWindowSession == null) {try {InputMethodManager imm = InputMethodManager.getInstance();IWindowManager windowManager = getWindowManagerService();sWindowSession = windowManager.openSession(new IWindowSessionCallback.Stub() {@Overridepublic 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 的通信機(jī)制,和 AMS 非常的類似想了解看下這篇 Android進(jìn)程間的通信 - IPC(機(jī)制)Binder的原理和源碼閱讀 ,現(xiàn)在我們?nèi)シ?wù)端 WindowManagerService 的 openSession 方法:
@Overridepublic 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我們總算是獲取到了,服務(wù)端 WindowManagerService 創(chuàng)建了一個 Session 對象返回回來了,接下來我們發(fā)現(xiàn) Session 的 addToDisplay 方法來到了 WindowManager 的 addWindow 方法,相信應(yīng)該差不多快完了吧,這不是人干的事:
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中檢查是否存在一個與參數(shù)client所對應(yīng)的WindowState對象,如果已經(jīng)存在,那么就說明WindowManagerService服務(wù)已經(jīng)為它創(chuàng)建過一個WindowState對象了,因此,這里就不會繼續(xù)往前執(zhí)行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_DUPLICATE_ADD。我們繼續(xù)往前看代碼:
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; } }參數(shù)attrs指向的是一個WindowManager.LayoutParams對象,用來描述正在啟動的Activity組件的UI布局,當(dāng)它的成員變量type的值大于等于FIRST_SUB_WINDOW并且小于等于LAST_SUB_WINDOW的時候,就說明現(xiàn)在要增加的是一個子窗口。在這種情況下,就必須要指定一個父窗口,而這個父窗口是通過數(shù)attrs指向的是一個WindowManager.LayoutParams對象的成員變量token來指定的,因此,這段代碼就會調(diào)用WindowManagerService類的另外一個成員函數(shù)windowForClientLocked來獲得用來描述這個父窗口的一個WindowState對象,并且保存在變量attachedWindow。
如果得到變量attachedWindow的值等于null,那么就說明父窗口不存在,這是不允許的,因此,函數(shù)就不會繼續(xù)向前執(zhí)行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN。另一方面,如果變量attachedWindow的值不等于null,但是它的成員變量mAttrs所指向的一個WindowManager.LayoutParams對象的成員變量type的值也是大于等于FIRST_SUB_WINDOW并且小于等于LAST_SUB_WINDOW,那么也說明找到的父窗口也是一個子窗口,這種情況也是不允許的,因此,函數(shù)就不會繼續(xù)向前執(zhí)行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN。
繼續(xù)看代碼:
// 一些檢查合法的代碼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; }通過上面的合法性檢查之后,這里就可以為正在增加的窗口創(chuàng)建一個WindowState對象了。 WindowManagerService類的成員變量mPolicy指向的是一個實(shí)現(xiàn)了WindowManagerPolicy接口的窗口管理策略器。在Phone平臺中,這個窗口管理策略器是由com.android.internal.policy.impl.PhoneWindowManager來實(shí)現(xiàn)的,它負(fù)責(zé)對系統(tǒng)中的窗口實(shí)現(xiàn)制定一些規(guī)則。這里主要是調(diào)用窗口管理策略器的成員函數(shù)adjustWindowParamsLw來調(diào)整當(dāng)前正在增加的窗口的布局參數(shù),以及調(diào)用成員函數(shù)prepareAddWindowLw來檢查當(dāng)前應(yīng)用程序進(jìn)程請求增加的窗口是否是合法的。如果不是合法的,即變量res的值不等于WindowManagerImpl.ADD_OKAY,那么函數(shù)就不會繼續(xù)向前執(zhí)行,而直接返回錯誤碼res。
我們就先看到這里了,你甚至還可以在去了解 WindowState 的成員變量都有一些啥作用,又或者是那些合法的檢查代碼都有什么用等等,最后我再畫一張草圖:
我花了大概半個多月的時間才勉強(qiáng)看懂一些源碼,當(dāng)然包括這篇文章中的所有源碼鏈接,如果看不太懂需要多花些時間,如果文字讓你受不了可以看看我的直播視頻,同時這也是自定義View部分的最后一篇文章了。
所有分享大綱:Android進(jìn)階之旅 - 自定義View篇
視頻講解地址:http://pan.baidu.com/s/1pLNXkxl
總結(jié)
以上是生活随笔為你收集整理的源码阅读分析 - Window底层原理与系统架构的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 论坛发帖推广方法详解
- 下一篇: java编一个求圆柱表面积_Java:输