Android Window機(jī)制探索

一.Window 概述

Window表示一個(gè)窗口的概念弹灭,Android手機(jī)中所有的視圖都是通過Window來呈現(xiàn)的围俘,像常用的Activity,Dialog,PopupWindow速兔,Toast,他們的視圖都是附加在Window上的活玲,所以可以這么說 ——「Window是View的直接管理者涣狗〉瘢」

window1.jpg

Window

一個(gè)頂級窗口查看和行為的一個(gè)抽象基類。這個(gè)類的實(shí)例作為一個(gè)頂級 View 添加到 Window Manager镀钓。它提供了一套標(biāo)準(zhǔn)的 UI 方法穗熬,比如添加背景,標(biāo)題等等丁溅。當(dāng)你需要用到 Window 的時(shí)候唤蔗,你應(yīng)該使用它的唯一實(shí)現(xiàn)類 PhoneWindow。

PhoneWindow

Window的具體實(shí)現(xiàn)類是PhoneWindow 在啟動Activity的attach方法中被創(chuàng)建窟赏,Activity中setContentView實(shí)際上是調(diào)用 PhoneWindow 的setContentView 方法妓柜。并且 PhoneWindow 中包含著成員變量 DecorView。

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

//getWindow獲取到的是mWindow          
//在attach方法里,mWindow = new PhoneWindow(this, window);

DecorView(FrameLayout)

作為頂級View,DecorView一般情況下它內(nèi)部會包含一個(gè)豎直方向的LinearLayout涯穷,上面的標(biāo)題欄(titleBar)棍掐,下面是內(nèi)容欄。通常我們在Activity中通過setContentView所設(shè)置的布局文件就是被加載到id為android.R.id.content的內(nèi)容欄里(FrameLayout)

二.setContentView

setContentView主要包含三個(gè)操作:1.初始化 2.填充Layout 3.通知Activity布局改變拷况。

//PhoneWindow 

    @Override 
    public void setContentView(int layoutResID) { 
        if (mContentParent == null) { 

//1.初始化 
        //創(chuàng)建DecorView對象和mContentParent對象  
            installDecor(); 
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { 
            mContentParent.removeAllViews();//Activity轉(zhuǎn)場動畫相關(guān) 
        } 

//2.填充Layout 
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { 
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, 
                    getContext()); 
            transitionTo(newScene);//Activity轉(zhuǎn)場動畫相關(guān) 
        } else { 
        //將Activity設(shè)置的布局文件作煌,加載到mContentParent中 
            mLayoutInflater.inflate(layoutResID, mContentParent); 
        } 

        //讓DecorView的內(nèi)容區(qū)域延伸到systemUi下方,防止在擴(kuò)展時(shí)被覆蓋赚瘦,達(dá)到全屏粟誓、沉浸等不同體驗(yàn)效果。 
        mContentParent.requestApplyInsets(); 

//3.通知Activity布局改變 
        final Callback cb = getCallback(); 
        if (cb != null && !isDestroyed()) { 

        //觸發(fā)Activity的onContentChanged方法   
            cb.onContentChanged(); 
        } 
        mContentParentExplicitlySet = true; 
    }

1.初始化:installDecor()---創(chuàng)建DecorView對象和mContentParent對象

這個(gè)方法主要完成DecorView的創(chuàng)建和mContentParent對象蚤告,前面已經(jīng)介紹過努酸,DecorView是Activity中的頂級View,包括TitleBar和ContentView杜恰。mContentParent是com.android.internal.R.id.content所對應(yīng)的ViewGroup即DecorView中的ContentView。

//PhoneWindow  --> setContentView() 
private void installDecor() { 
        if (mDecor == null) { 
        //調(diào)用該方法創(chuàng)建new一個(gè)DecorView 
            mDecor = generateDecor(); 
        }else { 
            mDecor.setWindow(this); 
        } 
        if (mContentParent == null) { 
        //根據(jù)主題theme設(shè)置對應(yīng)的xml布局文件以及Feature(包括style,layout,轉(zhuǎn)場動畫,屬性等)到DecorView中仍源。 
        //并將mContentParent綁定至id為ID_ANDROID_CONTENT(com.android.internal.R.id.content)的ViewGroup 
        //mContentParent在DecorView添加的xml文件中 
            mContentParent = generateLayout(mDecor); 
            // Set up decor part of UI to ignore fitsSystemWindows if appropriate. 
            mDecor.makeOptionalFitsSystemWindows();           
            //添加其他資源 
            //設(shè)置轉(zhuǎn)場動畫 
        } 
} 
mDecor = generateDecor()---創(chuàng)建DecorView
protected DecorView generateDecor(){
    return new DecorView(getContext(), -1);
}
mContentParent = generateLayout(mDecor);---創(chuàng)建mContentParent
//PhoneWindow --> setContentView()  -->installDecor()  


 protected ViewGroup generateLayout(DecorView decor) { 
        // Apply data from current theme. 
        //獲取當(dāng)前的主題心褐,加載默認(rèn)資源和布局 
        TypedArray a = getWindowStyle(); 

        ... 
        //根據(jù)theme的設(shè)定,找到對應(yīng)的Feature(包括style,layout,轉(zhuǎn)場動畫笼踩,屬性等) 
        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) { 
            requestFeature(FEATURE_NO_TITLE);//無titleBar 
        }  

        ... 

        if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) { 
            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));//設(shè)置全屏 
        } 

        if (a.getBoolean(R.styleable.Window_windowTranslucentStatus, 
                false)) { 
            setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS 
                    & (~getForcedWindowFlags()));//透明狀態(tài)欄 
        } 

        //根據(jù)當(dāng)前主題逗爹,設(shè)定不同的Feature 
        ... 

        int layoutResource; 
        int features = getLocalFeatures(); 

        //由于布局較多,我們拿有titleBar的例子來看 
        if ((features & (1 << FEATURE_NO_TITLE)) == 0) { 

            ... 
                layoutResource = R.layout.screen_title; 

        }  
        ... 
        else {//無titleBar 
            layoutResource = R.layout.screen_simple; 
        } 

        mDecor.startChanging(); 

        //將布局layout嚎于,添加至DecorView中 
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); 

        //從布局中獲取`ID_ANDROID_CONTENT`掘而,并關(guān)聯(lián)至contentParent 
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); 

        ... 
        //配置完成,DecorView根據(jù)已有屬性調(diào)整布局狀態(tài) 
        mDecor.finishChanging(); 

        return contentParent; 
    }

這個(gè)方法的前面會根據(jù)主題來做不同的處理于购,最后通過mDecor.onResourcesLoaded(mLayoutInflater, layoutResource)將布局layout添加至DecorView中袍睡,并關(guān)聯(lián)mContentParent。

整個(gè)setContentView的過程如下:
window2.jpg

二.Window的類型

Window有三種類型肋僧,分別是應(yīng)用Window斑胜、子Window控淡、系統(tǒng)window。應(yīng)用Window對應(yīng)著一個(gè)Activity止潘。子Window不能單獨(dú)存在掺炭,他需要附屬在特定的父Window之中,比如常見的一些Dialog凭戴。系統(tǒng)Window是要聲明權(quán)限才能創(chuàng)建的Window涧狮,比如Toast和系統(tǒng)狀態(tài)欄。

Window是分層的么夫,每個(gè)Window都有對應(yīng)的z-ordered者冤,層級大的會覆蓋在層級小的Window上面。在三類window中魏割,應(yīng)用Window的層級范圍是1-99譬嚣,子Window的層級范圍是1000-1999,系統(tǒng)Window的層級范圍是2000-2999钞它,這些層級范圍對應(yīng)這WindowManager.LayoutParam的type參數(shù)拜银。當(dāng)我們需要使用系統(tǒng)Window時(shí),需要聲明權(quán)限遭垛。

三.Window的內(nèi)部機(jī)制

Window是一個(gè)抽象的概念尼桶,每一個(gè)Window都對應(yīng)著一個(gè)View和ViewRootImpl,Window和View通過ViewRootImpl來建立聯(lián)系锯仪。因此Window并不是不存在的泵督,它是以View的形式存在。這點(diǎn)從ViewManager的定義也可以看得出來庶喜,它只提供三個(gè)接口方法:addView小腊、updateViewLayout、removeView久窟,這些方法都是針對View的秩冈。這說明View才是Window存在的實(shí)體。

Window的創(chuàng)建過程

首先要分析Window的創(chuàng)建過程斥扛,就必須了解Activity的啟動過程入问。
Activity的啟動過程很復(fù)雜,最終會由ActivityThread中的handleLaunchActivity()來完成整個(gè)啟動過程稀颁。
在這個(gè)方法中會通過performLaunchActivity()方法創(chuàng)建Activity芬失,performLaunchActivity()內(nèi)部通過類加載器創(chuàng)建Activity的實(shí)例對象,并調(diào)用其attach()方法為其關(guān)聯(lián)運(yùn)行過程中所依賴的一系列上下文環(huán)境變量以及創(chuàng)建與綁定窗口匾灶。

//ActivityThread

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ... 
    //獲取WindowManagerService的Binder引用(proxy端)棱烂。
    WindowManagerGlobal.initialize();    
    //會調(diào)用Activity的onCreate,onStart,onResotreInstanceState方法
    Activity a = performLaunchActivity(r, customIntent);    if (a != null) {
        ... 
        //會調(diào)用Activity的onResume方法.
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

        ... 
    } 

}

//ActivityThread

   private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {        
    //通過類加載器創(chuàng)建Activity
        Activity activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
        ...       

        //通過LoadedApk的makeApplication方法來創(chuàng)建Application對象
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);        
    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);
            ... 

            //onCreate
            mInstrumentation.callActivityOnCreate(activity, r.state);            
            //onStart
            activity.performStart();

        }        
            return activity;
    }

在Activity的attach()方法里,系統(tǒng)會創(chuàng)建Activity所屬的Window對象并為其設(shè)置回調(diào)接口粘昨,由于Activity實(shí)現(xiàn)了Window的Callback接口垢啼,因此當(dāng)Window接收到外界的狀態(tài)改變時(shí)就會回調(diào)Activity的方法窜锯。Callback接口中的方法很多,下面舉幾個(gè)比較眼熟的方法芭析。

public interface Callback { 

        public boolean dispatchTouchEvent(MotionEvent event); 

        public View onCreatePanelView(int featureId); 

        public boolean onMenuItemSelected(int featureId, MenuItem item); 

        public void onContentChanged(); 

        public void onWindowFocusChanged(boolean hasFocus); 

        public void onAttachedToWindow(); 

        public void onDetachedFromWindow(); 
    } 

//Activity 

final void attach(...) { 
        //綁定上下文 
        attachBaseContext(context); 

        //創(chuàng)建Window,PhoneWindow是Window的唯一具體實(shí)現(xiàn)類 
        mWindow = new PhoneWindow(this, window);
        //此處的window==null锚扎,但不影響 
        mWindow.setWindowControllerCallback(this); 
        mWindow.setCallback(this); 
       ...        //設(shè)置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()); 
        } 
        //創(chuàng)建完后通過getWindowManager就可以得到WindowManager實(shí)例 
        mWindowManager = mWindow.getWindowManager();//其實(shí)它是WindowManagerImpl 

    } 


    @Override 
    public Object getSystemService(@ServiceName @NonNull String name) { 
        ...        if (WINDOW_SERVICE.equals(name)) { 
            return mWindowManager; 
        }  
        return super.getSystemService(name); 
    }

在ActivityThread中的attach方法會創(chuàng)建Window的實(shí)現(xiàn)類PhoneWindow,并且通過getWindowManage得到WindowManager實(shí)例馁启,也就是WindowManagerImpl驾孔。
因?yàn)镻honeWindow中并沒有setWindowManager()方法,所以我們打開Window類看看惯疙。

//Window 
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, 
            boolean hardwareAccelerated) { 
    mAppToken = appToken; 
    mAppName = appName; 
    mHardwareAccelerated = hardwareAccelerated 
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false); 
    if (wm == null) { 
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); 
    } 
    //在此處創(chuàng)建mWindowManager  
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); 
} 
//在WindowManagerImpl類中 
public WindowManagerImpl createLocalWindowManager(Window parentWindow) { 
    return new WindowManagerImpl(mContext, parentWindow); 
}

到了這里Window已經(jīng)創(chuàng)建完畢翠勉,在上面的performLaunchActivity()方法中attach方法后我們可以看到調(diào)用了onCreate()方法:

//ActivityThread --> performLaunchActivity
mInstrumentation.callActivityOnCreate(activity, r.state);

回調(diào)Activity的onCreate方法,通過setContentView()將view添加到DecorView的mContentParent中霉颠,也就是將資源布局文件和phoneWindow關(guān)聯(lián)对碌。

經(jīng)過了上面幾個(gè)過程,Window和DecorView已經(jīng)被創(chuàng)建并初始化完畢蒿偎,Activity的布局文件也成功添加到了DecorView的mContentParent中朽们,但這個(gè)時(shí)候的DecorView還沒有被WindowManager正式添加到Window中。

這里需要理解的是诉位,Window更多表示的是一種抽象功能集合骑脱,雖然說早在Activity的attach方法中window就已經(jīng)被創(chuàng)建了,但是這個(gè)時(shí)候由于DecorView并沒有被WindowManager識別苍糠,所以這個(gè)時(shí)候的Window暫時(shí)無法提供具體功能叁丧。
總的來說,Window可以成功使用有2個(gè)標(biāo)志:
①View繪制完畢岳瞭,可以呈現(xiàn)給用戶拥娄。
②View可以接收外界信息(觸摸事件等)。

Window的添加過程

PhoneWindow 只是負(fù)責(zé)處理一些應(yīng)用窗口通用的邏輯(設(shè)置標(biāo)題欄瞳筏,導(dǎo)航欄等)条舔。但是真正完成把一個(gè) View,作為窗口添加到 WmS 的過程是由 WindowManager 來完成的乏矾。WindowManager 的具體實(shí)現(xiàn)是 WindowManagerImpl。

下面我們繼續(xù)來分析handleLaunchActivity()方法中handleResumeActivity()的執(zhí)行過程迁杨。

//ActivityThread 

final void handleResumeActivity(IBinder token, 
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { 

        //把a(bǔ)ctivity數(shù)據(jù)記錄更新到ActivityClientRecord 
        ActivityClientRecord r = mActivities.get(token); 

        r = performResumeActivity(token, clearHide, reason); 

        if (r != null) { 

            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; 

             ... 
                if (a.mVisibleFromClient && !a.mWindowAdded) { 
                    a.mWindowAdded = true; 
                    wm.addView(decor, l);//把decor添加到窗口上(劃重點(diǎn)) 
                } 
            }  
                //屏幕參數(shù)發(fā)生了改變 
                performConfigurationChanged(r.activity, r.tmpConfig); 

                WindowManager.LayoutParams l = r.window.getAttributes(); 

                    if (r.activity.mVisibleFromClient) { 
                        ViewManager wm = a.getWindowManager(); 
                        View decor = r.window.getDecorView(); 
                        wm.updateViewLayout(decor, l);//更新窗口狀態(tài) 
                    } 

                ...                if (r.activity.mVisibleFromClient) { 
                    //已經(jīng)成功添加到窗口上了(繪制和事件接收)钻心,設(shè)置為可見 
                    r.activity.makeVisible(); 
                } 
            //通知ActivityManagerService,Activity完成Resumed 
             ActivityManagerNative.getDefault().activityResumed(token); 
        }  
    }

在上面代碼中铅协,首先配置ActivityClientRecord捷沸,之后將DecorView設(shè)置為INVISIBLE,因?yàn)閂iew并未繪制完成狐史,當(dāng)前的DecorView只是一個(gè)有結(jié)構(gòu)的空殼痒给。

然后通過WindowManagerImpl將DecorView正式的添加到窗口上wm.addView(decor, l);说墨,這一步非常非常重要,因?yàn)樗?個(gè)比較重要和常見的過程:Window的添加過程和View的繪制流程苍柏。

window3.png

窗口的添加過程如上圖所示尼斧,我們知道 WmS 運(yùn)行在單獨(dú)的進(jìn)程中。這里 IWindowSession 執(zhí)行的 addtoDisplay 操作應(yīng)該是 IPC 調(diào)用试吁。接下來的Window添加過程棺棵,我們會知道每個(gè)應(yīng)用窗口創(chuàng)建時(shí),最終都會創(chuàng)建一個(gè) ViewRootImpl 對象熄捍。

ViewRootImpl 是一很重要的類烛恤,類似 ApplicationThread 負(fù)責(zé)跟AmS通信一樣,ViewRootImpl 的一個(gè)重要職責(zé)就是跟 WmS 通信余耽,它通靜態(tài)變量 sWindowSession(IWindowSession實(shí)例)與 WmS 進(jìn)行通信缚柏。

每個(gè)應(yīng)用進(jìn)程,僅有一個(gè) sWindowSession 對象碟贾,它對應(yīng)了 WmS 中的 Session 子類币喧,WmS 為每一個(gè)應(yīng)用進(jìn)程分配一個(gè) Session 對象。WindowState 類有一個(gè) IWindow mClient 參數(shù)缕陕,是由 Session 調(diào)用 addToDisplay 傳遞過來的粱锐,對應(yīng)了 ViewRootImpl 中的 W 類的實(shí)例。

簡單的總結(jié)一下扛邑,ViewRootImpl通過IWindowSession遠(yuǎn)程IPC通知WmS怜浅,并且由W類接收WmS的遠(yuǎn)程IPC通知。(這個(gè)W類和ActivityThread的H類同樣精悍的命名蔬崩,并且也是同樣的工作職責(zé)6褡)

圖解完Window的添加過程,對整個(gè)流程有一個(gè)印象和思路沥阳,那么下面繼續(xù)分析源碼跨琳。

在上面的handleResumeActivity()方法中,我們看到源碼通過wm.addView(decor, l);操作DecorView和WindowManager.LayoutParams桐罕。上面講解也說過脉让,因?yàn)閃indowManager是接口,真正具體實(shí)現(xiàn)類是windowManagerImpl功炮。

//WindowManagerImpl 


    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); 
    } 

    @Override 
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { 
        applyDefaultToken(params); 
        mGlobal.updateViewLayout(view, params); 
    } 

    @Override 
    public void removeView(View view) { 
        mGlobal.removeView(view, false); 
    }

我們看到這個(gè)WindowManagerImpl對于Window(或者可以說是View)的操作都是交由WindowManagerGlobal來處理溅潜,WindowManagerGlobal以工廠的形式向外提供自己的實(shí)例。這種工作模式是橋接模式薪伏,將所有的操作全部委托給WindowManagerGlobal來實(shí)現(xiàn)滚澜。
在WindowManagerImpl的全局變量中通過單例模式初始化了WindowManagerGlobal,也就是說一個(gè)進(jìn)程就只有一個(gè)WindowManagerGlobal對象嫁怀。

//WindowManagerGlobal 


   public void addView(View view, ViewGroup.LayoutParams params, 
            Display display, Window parentWindow) { 
        if (view == null) { 
            throw new IllegalArgumentException("view must not be null"); 
        } 
        if (display == null) { 
            throw new IllegalArgumentException("display must not be null"); 
        } 
        if (!(params instanceof WindowManager.LayoutParams)) { 
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 
        } 


        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; 
        if (parentWindow != null) { 
            //調(diào)整布局參數(shù)设捐,并設(shè)置token 
            parentWindow.adjustLayoutParamsForSubWindow(wparams); 
        }  

        ViewRootImpl root; 
        View panelParentView = null; 

        synchronized (mLock) { 


            int index = findViewLocked(view, false); 
            if (index >= 0) { 
                if (mDyingViews.contains(view)) { 
                    //如果待刪除的view中有當(dāng)前view借浊,刪除它 
                    // Don't wait for MSG_DIE to make it's way through root's queue. 
                    mRoots.get(index).doDie(); 
                } 
                // The previous removeView() had not completed executing. Now it has. 
                //之前移除View并沒有完成刪除操作,現(xiàn)在正式刪除該view 
            } 

            //如果這是一個(gè)子窗口個(gè)(popupWindow)萝招,找到它的父窗口蚂斤。 
            //最本質(zhì)的作用是使用父窗口的token(viewRootImpl的W類,也就是IWindow) 
            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); 
                    } 
                } 
            } 
            //創(chuàng)建ViewRootImpl即寒,并且將view與之綁定 
            root = new ViewRootImpl(view.getContext(), display); 

            view.setLayoutParams(wparams); 

            mViews.add(view);//將當(dāng)前view添加到mViews集合中 
            mRoots.add(root);//將當(dāng)前ViewRootImpl添加到mRoots集合中 
            mParams.add(wparams);//將當(dāng)前window的params添加到mParams集合中 
        } 

         ...            //通過ViewRootImpl的setView方法橡淆,完成view的繪制流程,并添加到window上母赵。 
            root.setView(view, wparams, panelParentView); 
    }

在WindowManagerGlobal的addView()方法里逸爵,最后調(diào)用ViewRootImpl的setView方法,處理添加過程凹嘲。

//WindowManagerGlobal  -->  addView 
      //創(chuàng)建ViewRootImpl师倔,并且將view與之綁定 
      root = new ViewRootImpl(view.getContext(), display); 

      //通過ViewRootImpl的setView方法,完成view的繪制流程周蹭,并添加到window上趋艘。 
      root.setView(view, wparams, panelParentView);

通過上面這個(gè)代碼可知,WindowManagerGlobal將View的處理操作全權(quán)交給ViewRootImpl凶朗,而且上面我們也提到了瓷胧,View成功添加到Window,無非就是展現(xiàn)視圖和用戶交互棚愤。

①View繪制完畢搓萧,可以呈現(xiàn)給用戶。
②View可以接收外界信息(觸摸事件等)宛畦。

在ViewRootImpl的setView()方法中瘸洛,將會完成上面2個(gè)艱巨而又偉大的任務(wù)。

//ViewRootImpl  


  public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { 

                int res;  

                // 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();//View的繪制流程 

                if ((mWindowAttributes.inputFeatures 
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { 
                    //創(chuàng)建InputChannel 
                    mInputChannel = new InputChannel(); 
                } 

                try { 

                    //通過WindowSession進(jìn)行IPC調(diào)用次和,將View添加到Window上 
                    //mWindow即W類反肋,用來接收WmS信息 
                    //同時(shí)通過InputChannel接收觸摸事件回調(diào) 
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, 
                            getHostVisibility(), mDisplay.getDisplayId(), 
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, 
                            mAttachInfo.mOutsets, mInputChannel); 
                } 

                ... 

                    //處理觸摸事件回調(diào) 
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, 
                            Looper.myLooper()); 

                ... 
    }

在ViewRootImpl的setView()方法里,執(zhí)行requestLayout()方法完成View的繪制流程踏施,并且通過WindowSession將View和InputChannel添加到WmS中石蔗,從而將View添加到Window上并且接收觸摸事件。

通過mWindowSession來完成Window的添加過程 畅形,mWindowSession的類型是IWindowSession抓督,是一個(gè)Bindler對象,真正的實(shí)現(xiàn)類是Session束亏,也就是Window的添加是一次IPC調(diào)用。(mWindowSession在ViewRootImpl的構(gòu)造函數(shù)中通過WindowManagerGlobal.getWindowSession();創(chuàng)建)

同時(shí)將mWindow(即 W extends IWindow.Stub)發(fā)送給WmS阵具,用來接收WmS信息碍遍。

window4.png
//Session 


    final WindowManagerService mService; 

   @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); 
    }

如此一來定铜,Window的添加請求就交給WmS去處理了,在WmS內(nèi)部會為每一個(gè)應(yīng)用保留一個(gè)單獨(dú)的Session怕敬。在WmS 端會創(chuàng)建一個(gè)WindowState對象用來表示當(dāng)前添加的窗口揣炕。 WmS負(fù)責(zé)管理這里些 WindowState 對象。至此东跪,Window的添加過程就結(jié)束了畸陡。

至于Window的刪除和更新過程,舉一反三虽填,也是使用WindowManagerGlobal對ViewRootImpl的操作丁恭,最終也是通過Session的IPC跨進(jìn)程通信通知到WmS。整個(gè)過程的本質(zhì)都是同出一轍的斋日。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末牲览,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子恶守,更是在濱河造成了極大的恐慌第献,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兔港,死亡現(xiàn)場離奇詭異庸毫,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)衫樊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門飒赃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人橡伞,你說我怎么就攤上這事盒揉。” “怎么了兑徘?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵刚盈,是天一觀的道長。 經(jīng)常有香客問我挂脑,道長藕漱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任崭闲,我火速辦了婚禮肋联,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刁俭。我一直安慰自己橄仍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著侮繁,像睡著了一般虑粥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宪哩,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天娩贷,我揣著相機(jī)與錄音,去河邊找鬼锁孟。 笑死彬祖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的品抽。 我是一名探鬼主播储笑,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼桑包!你這毒婦竟也來了南蓬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤哑了,失蹤者是張志新(化名)和其女友劉穎赘方,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弱左,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡窄陡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拆火。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跳夭。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖们镜,靈堂內(nèi)的尸體忽然破棺而出币叹,到底是詐尸還是另有隱情,我是刑警寧澤模狭,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站贩汉,受9級特大地震影響匹舞,放射性物質(zhì)發(fā)生泄漏赐稽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧躏将,春花似錦祸憋、人聲如沸蚯窥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至允乐,卻和暖如春削咆,著一層夾襖步出監(jiān)牢的瞬間拨齐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留馁害,地道東北人碘菜。 一個(gè)月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓仰坦,卻偏偏與公主長得像计雌,于是被迫代替她去往敵國和親妈橄。 傳聞我的和親對象是個(gè)殘疾皇子翁脆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容