捋一捋,到底怎么樣去理解Window機(jī)制?

說到 Window 機(jī)制,通常想到的就是PhoneWindowViewRootImpl、WindowManagerImpl、子窗口、DecorView 等等,網(wǎng)上也有不少博客通過源碼分析他們之間的調(diào)用關(guān)系,可是能說得比較清楚的卻不多,或深入源碼不可自拔,或越說越復(fù)雜概念一大堆。

今天,我們就來好好捋一捋,到底怎么樣去理解Window機(jī)制呢?

先撇開復(fù)雜源碼與難懂的概念,我們現(xiàn)在談的Window都是單純地指Window這個(gè)類,請讀者暫時(shí)拋開"窗口"、“子窗口”、 “子Window 這些擾亂思路的概念,文章后面也會對這些概念以及應(yīng)用作出說明和解釋,所以先看看Window這個(gè)類,由簡單到復(fù)雜。(" Window" 可以視為一個(gè)錯(cuò)誤的說法,這詞在對理解Window機(jī)制上沒什么好處,所以請永遠(yuǎn)忘記它!?。。?/p>

為了有更好的閱讀思路,這里先給出幾條結(jié)論:

  1. 每個(gè)Window都有自己的唯一的一個(gè)WindowManager,WindowManager負(fù)責(zé)維護(hù)它所在的Window里面的內(nèi)容。
  2. Android平臺上Window的具體實(shí)現(xiàn)是PhoneWindow、對應(yīng)它的WindowManagerWindowManagerImpl
  3. WindowManagerImpl維護(hù)PhoneWindow中的什么內(nèi)容呢? 答案:DecorView及其子View所組成的View樹。
  4. 一個(gè)Activity有且只有一個(gè)PhoneWindow實(shí)例,一個(gè)Dialog同樣有且只有一個(gè)PhoneWindow實(shí)例。

可能聰明的同學(xué)就會問了: 怎么沒看到你的理解中有ViewRootImpl、WindowManagerGlobal這些呢?我給張圖你就明白了。

image.png

如上圖:

  • 一個(gè)Window都有一個(gè)WindowManager負(fù)責(zé)管理它的內(nèi)容。
  • PhoneWindow的內(nèi)容則交給WindowManagerImpl管理,其中的“內(nèi)容”指的則是DecorView及其子View所組成的View樹。
  • 當(dāng)WindowManagerImpl決定管理View樹時(shí),會給這個(gè)View樹分配一個(gè)ViewRootImpl,ViewRootImpl則是負(fù)責(zé)與其他服務(wù)聯(lián)絡(luò)并指導(dǎo)View樹的繪制工作。
  • ActivityPhoneWindow對象,Dialog同樣也有自己的PhoneWindow對象

Window與WindowManager

雖然在 Android 平臺上PhoneWindowWindow的唯一實(shí)現(xiàn),可是直接跳過Window直接看PhoneWindow這種做法并不值得提倡。
打開Window的源碼,會發(fā)現(xiàn)它有一個(gè)WindowManager對象。這個(gè)WindowManager就負(fù)責(zé)管理當(dāng)前Window對象的內(nèi)容。

//: Window.java

public abstract class Window {
    ...
    private WindowManager mWindowManager;
    ...
    
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated;
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }
}

所以,不管Window的實(shí)現(xiàn)類是不是PhoneWindow,它都會有一個(gè)它自己的管理者WindowManager管理它的內(nèi)部。

image.png

Activity 與 PhoneWindow

接下來再看看Window的唯一實(shí)現(xiàn)PhoneWindow。在 Android 中脫離 ActivityPhoneWindow都是耍流氓。

所以這里將主要介紹Actitity啟動過程中,PhoneWinow的創(chuàng)建流程、WindowManagerImpl的綁定、DecorView的創(chuàng)建以及ViewRootImpl的分配。

image.png

創(chuàng)建PhoneWindow

首先一起看一下PhoneWindow是何時(shí)創(chuàng)建的。眾所周知,Activity 啟動時(shí) ActivityThread 會調(diào)用 performLaunchActivity()方法創(chuàng)建一個(gè)Activity實(shí)例,緊接著會調(diào)用它的 attach 方法。

//: ActivityThread.java

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
         Activity activity = null;
         ...
         java.lang.ClassLoader cl = appContext.getClassLoader();
         activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
         Application app = r.packageInfo.makeApplication(false, mInstrumentation);
         ...
         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, r.configCallback,
                        r.assistToken);
    }

Activityattach 方法當(dāng)中,PhoneWindow被創(chuàng)建并賦值給 Activity 的成員變量mWindow,緊接著會為PhoneWindow設(shè)置一個(gè)WindowManager

//: Activity.java

    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, IBinder assistToken) {
        ...
        //創(chuàng)建PhoneWindow對象
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        ...
        //為PhoneWindow設(shè)置WindowManager
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        
        mWindowManager = mWindow.getWindowManager();
    }
//: Window.java

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        ...
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        //創(chuàng)建WindowManagerImpl實(shí)例,并賦值給Window的成員變量mWindowManager
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

//: WindowManagerImpl.java

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

可以看到Activity并不是天生就有PhoneWindow的,只是在啟動過程中Activityattch()方法被調(diào)用,然后才會給Activity創(chuàng)建PhoneWindow對象,并在PhoneWindow創(chuàng)建后緊接著給它新建一個(gè)WindowManagerImpl。

attch()完成后,Activity 就是這個(gè)樣子了。

image.png

創(chuàng)建DecorView

當(dāng)PhoneWindow及其內(nèi)容管理者WindowManagerImpl創(chuàng)建好了以后,就需要關(guān)心管理者所管理的View樹是何時(shí)創(chuàng)建了。也就是DecorView何時(shí)創(chuàng)建,這一點(diǎn)大部分讀者都比較熟悉,在 Activity 中調(diào)用 setContentView()時(shí),DecorView會被創(chuàng)建。

//: Activity.java

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

上述的 getWindow().setContentView(layoutResID)實(shí)際調(diào)用的就是PhoneWindowsetContentView方法。其源碼如下:

//: PhoneWondow.java

    @Override
    public void setContentView(int layoutResID) {
        // 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) {
            //創(chuàng)建DecorView
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        ...
    }
    
    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            //創(chuàng)建DecorView并賦值給PhoneWindow的成員變量mDecor
            mDecor = generateDecor(-1);
            ...
        }
        ...
    }
    
    protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, this);
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        //新建DecorView對象,并將PhoneWindow實(shí)例傳入其中
        return new DecorView(context, featureId, this, getAttributes());
    }

完成setContent后,PhoneWindow也有自己的DecorView了。不過到目前為止,雖然ActivityPhoneWindow,有PhoneWindow也有WindowManagerImplDecorView(或View樹)了,此時(shí)的 Activity仍然不會在界面上顯示任何東西。

要在界面上顯示View樹,必須要通知WindowManagerServices才可以,這個(gè)通知工作在是在ViewRootImpl之中完成,然而目前還未曾創(chuàng)建ViewRootImpl。

image.png

創(chuàng)建ViewRootImpl

上面講到在View樹顯示到界面之前,需要有一個(gè)ViewRootImpl負(fù)責(zé)指導(dǎo)它的繪制顯示工作。那這個(gè)ViewRootImpl是何時(shí)創(chuàng)建并與View樹關(guān)聯(lián)的呢?

一句話簡要概況:當(dāng)WindowManagerImpl決定管理View樹時(shí),會給它關(guān)聯(lián)一個(gè)ViewRootImpl實(shí)例。

從代碼層面來說就是WindowManagerImpl調(diào)用addView方法將View樹添加到View列表中時(shí)。

先看一下WindowManagerImpladdView方法:

//: WindowManagerImpl.java

    //單例 WindowManagerGlobal
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

可以看到所有的WindowManagerImplView的管理都是交給一個(gè)唯一的WindowManagerGlobal了。所以,我們需要再看看WindowManagerGlobaladdView方法:

//: WindowManagerGlobal.java
    private static IWindowSession sWindowSession;
    
    private final ArrayList<View> mViews = new ArrayList<View>();
    @UnsupportedAppUsage
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    @UnsupportedAppUsage
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
            
    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) {
            ...
            // 創(chuàng)建ViewRootImpl實(shí)例
            root = new ViewRootImpl(view.getContext(), display);
            // 給View設(shè)置屬性
            view.setLayoutParams(wparams);
            
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            ...
            //將ViewRootImp于View進(jìn)行綁定
            root.setView(view, wparams, panelParentView);
            ...
        }
    }
    

可以看到當(dāng)WindowManagerGlobaladdView方法被調(diào)用時(shí),ViewRootImpl被創(chuàng)建并將其與View樹進(jìn)行綁定。ViewRootImplView樹進(jìn)行綁定后,Activity看起來就是這個(gè)樣子了。

image.png

看到這里又有同學(xué)要舉手提問了,這個(gè)Activity何時(shí)會讓WindowManagerImpl執(zhí)行addView操作呢?

為了解決這個(gè)問題?我們需要再回到Activity的啟動流程,觀察ActivityThreadhandleResumeActivity方法:

//: ActivityThread.java

    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        ...
        // TODO Push resumeArgs into the activity for consideration
        //內(nèi)部會調(diào)用Activity的onResume方法
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        
        final Activity a = r.activity;

        final int forwardBit = isForward
                ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

        // If the window hasn't yet been added to the window manager,
        // and this guy didn't finish itself or start another activity,
        // then go ahead and add the window.
        boolean willBeVisible = !a.mStartedActivity;
        if (!willBeVisible) {
            try {
                willBeVisible = ActivityTaskManager.getService().willActivityBeVisible(
                        a.getActivityToken());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        //從willBeVisible的注釋中可以了解到當(dāng)窗口沒有被添加到WindowManager中時(shí)willBeVisible為true
        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;
            //窗口類型設(shè)置
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
           
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    //WindowManagerImpl將View添加到自己的管理隊(duì)列中
                    wm.addView(decor, l);
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }

        }
    }

閱讀完handleResumeActivity方法后,WindowManagerImpladdView方法正式在此方法中完成的。

流程有點(diǎn)長,先小結(jié)一下之前的內(nèi)容:

  1. 第一步:在Activity的啟動過程中,ActivityThread會創(chuàng)建一個(gè)Activity實(shí)例。
  2. 第二步:在Activity實(shí)例創(chuàng)建完成后,緊接著執(zhí)行Activity#attach()方法。
  3. 第三步:在Activity#attach()方法之內(nèi)PhoneWindow被創(chuàng)建,并同時(shí)創(chuàng)建一個(gè)WindowManagerImpl負(fù)責(zé)維護(hù)PhoneWindow內(nèi)的內(nèi)容。
  4. 第四部:在Activity#onCreate()中調(diào)用setContent()方法,這個(gè)方法內(nèi)部創(chuàng)建一個(gè)DecorView實(shí)例作為PhoneWindow的內(nèi)容。
  5. 第五步:在ActivityThread#handleResumeActivity()方法中WindowManagerImpl#addView被調(diào)用,WindowManagerImpl決定管理DecorView,并創(chuàng)建一個(gè)ViewRootImpl實(shí)例,將ViewRootImplView樹進(jìn)行關(guān)聯(lián),這樣ViewRootImpl就可以指揮View樹的具體工作。

通過后面的學(xué)習(xí)可以了解到,ViewRootImpl指揮View樹的這些工作包含:View樹的顯示、測量繪制、同步刷新以及事件分發(fā)。

驚不驚喜,意不意外???ViewRootImpl原來這么重要,傳說的眾View之父呀!

仔細(xì)想一下,就知道其實(shí)沒什么意外的~~~

ViewRootImpl與View

剛剛我們交代了ViewRootImpl的創(chuàng)建時(shí)機(jī),同時(shí)也講到View樹的顯示、測量繪制、同步刷新以及事件分發(fā)這些工作與ViewRootImpl聯(lián)系緊密。

ViewRootImpl再看看它的具體實(shí)現(xiàn):

//: ViewRootImpl.java

    final IWindowSession mWindowSession;
    final W mWindow;
    Choreographer mChoreographer;
 
    WindowInputEventReceiver mInputEventReceiver;
    
    public ViewRootImpl(Context context, Display display) {
        ...
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mWindow = new W(this);
        mChoreographer = Choreographer.getInstance();
        ...
    }
    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.
                //WMS添加窗口之前,進(jìn)行一次測量布局工作,這樣才能保證各類系統(tǒng)事件與View位置對應(yīng)關(guān)系的準(zhǔn)確性
                requestLayout();
                //通過mWindowSession通知WMS添加并顯示窗口
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
              ......
              //初始化事件接收器,
              mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
              //將當(dāng)前ViewRootImpl實(shí)例賦值給DecorView的mParent變量
              //眾View之父,由此而來
              view.assignParent(this);
  }
                            

ViewRootImpl中有mIWindowSession、mWindowmChoreographer、mInputEventReceiver三個(gè)比較重要的變量。

mIWindowSession與mWindow

mIWindowSession、mWindowBinder對象,用于APP端與WMS之間的相互通信。細(xì)心的讀者可能會發(fā)現(xiàn),在ViewRootImpl對象創(chuàng)建后緊接著有一個(gè)ViewRootImplsetView操作。不難發(fā)現(xiàn),在ViewRootImpl#setView中使用了IWindowSession.addToDisplay來通知WMS添加并顯示窗口。對WMS添加窗口細(xì)節(jié)感興趣的讀者可以參看《WindowManagerService窗口管理之Window添加流程》

image.png

關(guān)于APP端使用mIWindowSessionmWindow與WMS通訊細(xì)節(jié)的問題,可以參考文章

mChoreographer

對于部分同學(xué)來說Choreographer可能并不陌生,我們通常叫做“編舞者”。無論是系統(tǒng)同步刷新、ViewrequestLayout還是界面幀率監(jiān)控,都能看到Choreographer的身影。
Choreographer要是展開講的話,內(nèi)容太長。希望讀者可以去看看袁輝輝大佬的《Choreographer原理》,或者也可以看看這篇《Android 怎么就不卡了呢之Choreographer》或者《你真的了解16.6ms刷新機(jī)制嗎?》

mInputEventReceiver

最后一點(diǎn)要講的就是WindowInputEventReceiver在事件分發(fā)事件中的作用??赡芤徽劦绞录职l(fā),部分同學(xué)就會想到View的事件分發(fā),可是View的事件是誰傳給它的呢?由于筆者水平有限,強(qiáng)烈推薦袁輝輝大佬的《Input系統(tǒng)—事件處理全過程》,如果覺得難度太大的話可以先看看《原來Android觸控機(jī)制竟是這樣的?》。

子窗口

一說到Window機(jī)制,不少同學(xué)就喜歡聊子窗口,可是我卻不喜歡聊它。為啥?在我看來這個(gè)概念太務(wù)虛了,就像去了解“茴香豆”的“茴”字有幾種寫法是一個(gè)道理,咬文嚼字不值得提倡。另外一點(diǎn),它干擾了我們理解Window機(jī)制。

首先,什么是窗口?警告:一個(gè)窗口本質(zhì)上是一個(gè)View樹,就像ActivityDecorView及其子View組成的一顆View樹,就可以視作是Activity內(nèi)的一個(gè)窗口,注意它并不是Window對象。

之前我們講到Activity啟動過程中,會創(chuàng)建一個(gè)PhoneWindow與對應(yīng)的WindowManagerImpl對象,然后在setContent的時(shí)候會給PhoneWindow創(chuàng)建一個(gè)DecorView,最終在WindowManagerImpl#addView中給DecorView分配一個(gè)ViewRootImpl對象負(fù)責(zé)指導(dǎo)DecorView的工作。這個(gè)DecorView及其子View組成的View樹就是一個(gè)窗口。它的窗口類型為WindowManager.LayoutParams.TYPE_BASE_APPLICATION(在ActivityThread#handleResumeActivity()方法中被設(shè)置的),所以我們把它叫做應(yīng)用窗口。

那什么是子窗口?

PhoneWindowWindowManagerImpl創(chuàng)建好了以后,我們自己也可以調(diào)用WindowManagerImpl#addView來添加一個(gè)View樹,也叫添加窗口。

當(dāng)它的窗口類型處于WindowManager.LayoutParams.FIRST_SUB_WINDOWWindowManager.LayoutParams.LAST_SUB_WINDOW之間時(shí),我們稱這個(gè)直接添加的窗口為子窗口。

當(dāng)然,也有的同學(xué)把直接調(diào)用WindowManagerImpl#addView創(chuàng)建的窗口都叫做子窗口。本文并未采取這種方式定義子窗口這一概念。

那這些直接通過WindowManagerImpl#addView創(chuàng)建的窗口公用同一個(gè)PhoneWindow以及WindowManagerImpl對象,窗口類型不同決定了它們在WMS上的一個(gè)z-order順序。

PopupWindow

接下來讓我們看看子窗口是如何添加的,以PopupWindow為例:

//: PopupWindow.java

private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;

    public void showAtLocation(View parent, int gravity, int x, int y) {
        mParentRootView = new WeakReference<>(parent.getRootView());
        //這個(gè)getWindowToken返回的就是ViewRootImpl里的mWindow對象
        showAtLocation(parent.getWindowToken(), gravity, x, y);
    }
    
        public void showAtLocation(IBinder token, int gravity, int x, int y) {
        if (isShowing() || mContentView == null) {
            return;
        }

        detachFromAnchor();

        mIsShowing = true;
        mIsDropdown = false;
        mGravity = gravity;
        //創(chuàng)建窗口屬性,配置窗口類型
        final WindowManager.LayoutParams p = createPopupLayoutParams(token);
        preparePopup(p);

        p.x = x;
        p.y = y;

        invokePopup(p);
    }
    
    protected final WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {
        final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
        p.gravity = computeGravity();
        p.flags = computeFlags(p.flags);
        p.type = mWindowLayoutType;
        p.token = token;
        p.softInputMode = mSoftInputMode;
        p.windowAnimations = computeAnimationResource();

        return p;
    }
    //添加窗口
    private void invokePopup(WindowManager.LayoutParams p) {
        if (mContext != null) {
            p.packageName = mContext.getPackageName();
        }

        final PopupDecorView decorView = mDecorView;
        decorView.setFitsSystemWindows(mLayoutInsetDecor);

        setLayoutDirectionFromAnchor();
        //添加窗口
        mWindowManager.addView(decorView, p);

        if (mEnterTransition != null) {
            decorView.requestEnterTransition(mEnterTransition);
        }
    }  

可以看到PopupWindow通過WindowManagerImpl直接添加了一個(gè)類型為WindowManager.LayoutParams.TYPE_APPLICATION_PANEL類型的窗口。

這個(gè)mWindowManager就是ActivityWindowManager,也就是Activity內(nèi)PhoneWindowWindowManagerImpl,所以,這個(gè)窗口要依附于Activity。Application沒有WindowManager所以不能被依附。

另一方面,這個(gè)窗口類型恰好是一個(gè)FIRST_SUB_WINDOW類型,所以PopupWindow是一個(gè)真正的子窗口。

//: WindowManager.java
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;`

Dialog

看完了PopupWindow再來看看Dialog有何不同?

//: Dialog.java

    Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        //創(chuàng)建PhoneWindow
        final Window w = new PhoneWindow(mContext);
        mWindow = w;
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setOnWindowSwipeDismissedCallback(() -> {
            if (mCancelable) {
                cancel();
            }
        });
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.CENTER);

        mListenersHandler = new ListenersHandler(this);
    }
    
    public void show() {
        if (mShowing) {
            if (mDecor != null) {
                if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
                    mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
                }
                mDecor.setVisibility(View.VISIBLE);
            }
            return;
        }
        ......
        onStart();
        mDecor = mWindow.getDecorView();

        WindowManager.LayoutParams l = mWindow.getAttributes();
        //添加窗口
        mWindowManager.addView(mDecor, l);
    }

可以看到,DialogPopupWindow不同,它有自己的PhoneWindow對象,同時(shí)Dialog的窗口類型為TYPE_APPLICATION,所以不能視為一個(gè)子窗口。

總結(jié)

文章篇幅較長,最后我們再總結(jié)一下。

無論是Activity還是PopupWindow,又或者Dialog。這些對象需要顯示到界面,都是通過調(diào)用WindowManagerImpl#addView來間接完成,它們所顯示的內(nèi)容實(shí)際就是View樹,這個(gè)View樹我們稱之為“窗口”。addView時(shí),會生成一個(gè)ViewRootImplView樹進(jìn)行關(guān)聯(lián),ViewRootImpl內(nèi)部則通過WindowSession來與WMS進(jìn)行通訊最終完成顯示。另一方面,WMS通過一個(gè)ViewRootImpl$W實(shí)例(mWindow)代理,將WMS端的事件傳遞到ViewRootImpl,最終交給View樹。

參考

Choreographer原理

原來Android觸控機(jī)制竟是這樣的?

Input系統(tǒng)—事件處理全過程

Android 怎么就不卡了呢之Choreographer

Android 應(yīng)用程序建立與WMS服務(wù)之間的通信過程

Android事件分發(fā)機(jī)制前篇——事件如何傳遞到Activity中

你真的了解16.6ms刷新機(jī)制嗎?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容