WMS(一):Window的添加過(guò)程

作者:劉望舒
鏈接:http://www.reibang.com/p/aadfb70f25e2

前言

在此前的系列文章中我們學(xué)習(xí)了WindowManager體系和Window的屬性售貌,這一篇我們接著來(lái)講Window的添加過(guò)程置鼻。建議閱讀此篇文章前先閱讀本系列的前兩篇文章驯妄。

1.概述

WindowManager對(duì)Window進(jìn)行管理,說(shuō)到管理那就離不開對(duì)Window的添加横侦、更新和刪除的操作,在這里我們把它們統(tǒng)稱為Window的操作簿透。對(duì)于Window的操作羹令,最終都是交由WMS來(lái)進(jìn)行處理。窗口的操作分為兩大部分描焰,一部分是WindowManager處理部分媳否,另一部分是WMS處理部分。我們知道Window分為三大類荆秦,分別是:Application Window(應(yīng)用程序窗口)、Sub Windwow(子窗口)和System Window(系統(tǒng)窗口)力图,對(duì)于不同類型的窗口添加過(guò)程會(huì)有所不同步绸,但是對(duì)于WMS處理部分,添加的過(guò)程基本上是一樣的吃媒, WMS對(duì)于這三大類的窗口基本是“一視同仁”的瓤介。

img

本篇主要會(huì)講解Window的操作的WindowManager處理部分吕喘,至于WMS處理部分會(huì)在后續(xù)的解析WMS系列文章中進(jìn)行講解。

2.系統(tǒng)窗口的添加過(guò)程

三大類窗口的添加過(guò)程會(huì)有所不同刑桑,這里以系統(tǒng)窗口StatusBar為例氯质,StatusBar是SystemUI的重要組成部分,具體就是指系統(tǒng)狀態(tài)欄祠斧,用于顯示時(shí)間闻察、電量和信號(hào)等信息。我們來(lái)查看StatusBar的實(shí)現(xiàn)類PhoneStatusBar的addStatusBarWindow方法琢锋,這個(gè)方法負(fù)責(zé)為StatusBar添加Window辕漂,如下所示。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java

 private void addStatusBarWindow() {
    makeStatusBarView();//1
    mStatusBarWindowManager = new StatusBarWindowManager(mContext);
    mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,
            mHeadsUpManager);
    mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());//2
}
                    

注釋1處用于構(gòu)建StatusBar的視圖吴超。在注釋2處調(diào)用了StatusBarWindowManager的add方法钉嘹,并將StatusBar的視圖(StatusBarWindowView)和StatusBar的傳進(jìn)去。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java

public void add(View statusBarView, int barHeight) {
    mLp = new WindowManager.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            barHeight,
            WindowManager.LayoutParams.TYPE_STATUS_BAR,//1
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                    | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
            PixelFormat.TRANSLUCENT);
    mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
    mLp.gravity = Gravity.TOP;
    mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
    mLp.setTitle("StatusBar");
    mLp.packageName = mContext.getPackageName();
    mStatusBarView = statusBarView;
    mBarHeight = barHeight;
    mWindowManager.addView(mStatusBarView, mLp);//2
    mLpChanged = new WindowManager.LayoutParams();
    mLpChanged.copyFrom(mLp);
}

首先通過(guò)創(chuàng)建LayoutParams來(lái)配置StatusBar視圖的屬性鲸阻,包括Width跋涣、Height、Type鸟悴、 Flag陈辱、Gravity、SoftInputMode等遣臼,不了Window屬性的請(qǐng)查看Android解析WindowManager(二)Window的屬性這篇文章性置。 關(guān)鍵在注釋1處,設(shè)置了TYPE_STATUS_BAR揍堰,表示StatusBar視圖的窗口類型是狀態(tài)欄鹏浅。在注釋2處調(diào)用了WindowManager的addView方法,addView方法定義在WindowManager的父類接口ViewManager中屏歹,而實(shí)現(xiàn)addView方法的則是WindowManagerImpl中隐砸,如下所示。
frameworks/base/core/java/android/WindowManagerImpl.java

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

在WindowManagerImpl的addView方法中蝙眶,接著會(huì)調(diào)用WindowManagerGlobal的addView方法:

frameworks/base/core/java/android/view/WindowManagerGlobal.java

  public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
      ...//參數(shù)檢查
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);//1
        } else {
        ...
        }

        ViewRootImpl root;
        View panelParentView = null;
         ...
            root = new ViewRootImpl(view.getContext(), display);//2
            view.setLayoutParams(wparams);
            mViews.add(view);
            mRoots.add(root);//3
            mParams.add(wparams);
        }
        try {
            root.setView(view, wparams, panelParentView);//4
        } catch (RuntimeException e) {
           ...
        }
    }

首先會(huì)對(duì)參數(shù)view季希、params和display進(jìn)行檢查。注釋1處幽纷,如果當(dāng)前窗口要作為子窗口式塌,就會(huì)根據(jù)父窗口對(duì)子窗口的WindowManager.LayoutParams類型的wparams對(duì)象進(jìn)行相應(yīng)調(diào)整。注釋2處創(chuàng)建了ViewRootImp并賦值給root友浸,緊接著在注釋3處將root存入到ArrayList<ViewRootImpl>類型的mRoots中峰尝,除了mRoots,mViews和mParams也是ArrayList類型的收恢,分別用于存儲(chǔ)窗口的view對(duì)象和WindowManager.LayoutParams類型的wparams對(duì)象武学。注釋4處調(diào)用了ViewRootImpl的setView方法祭往。
ViewRootImpl身負(fù)了很多職責(zé):

  • View樹的根并管理View樹
  • 觸發(fā)View的測(cè)量、布局和繪制
  • 輸入事件的中轉(zhuǎn)站
  • 管理Surface
  • 負(fù)責(zé)與WMS進(jìn)行進(jìn)程間通信

frameworks/base/core/java/android/view/ViewRootImpl.java

 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
           ...
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } 
                ...
    }

setView方法中有很多邏輯火窒,這里只截取了一小部分硼补,主要就是調(diào)用了mWindowSession的addToDisplay方法。mWindowSession是IWindowSession類型的熏矿,它是一個(gè)Binder對(duì)象已骇,用于進(jìn)行進(jìn)程間通信,IWindowSession是Client端的代理曲掰,它的Server端的實(shí)現(xiàn)為Session疾捍,此前包含ViewRootImpl在內(nèi)的代碼邏輯都是運(yùn)行在本地進(jìn)程的,而Session的addToDisplay方法則運(yùn)行在WMS所在的進(jìn)程栏妖。
frameworks/base/services/core/java/com/android/server/wm/Session.java

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

addToDisplay方法中會(huì)調(diào)用了WMS的addWindow方法乱豆,并將自身也就是Session,作為參數(shù)傳了進(jìn)去吊趾,每個(gè)應(yīng)用程序進(jìn)程都會(huì)對(duì)應(yīng)一個(gè)Session宛裕,WMS會(huì)用ArrayList來(lái)保存這些Session。這樣剩下的工作就交給WMS來(lái)處理论泛,在WMS中會(huì)為這個(gè)添加的窗口分配Surface揩尸,并確定窗口顯示次序,可見(jiàn)負(fù)責(zé)顯示界面的是畫布Surface屁奏,而不是窗口本身岩榆。WMS會(huì)將它所管理的Surface交由SurfaceFlinger處理,SurfaceFlinger會(huì)將這些Surface混合并繪制到屏幕上坟瓢。
窗口添加的WMS處理部分會(huì)在后續(xù)介紹WMS的系列文章進(jìn)行講解勇边,系統(tǒng)窗口的添加過(guò)程的時(shí)序圖如下所示。

img

3.Activity的添加過(guò)程

無(wú)論是哪種窗口折联,它的的添加過(guò)程在WMS處理部分中基本是類似的粒褒,只不過(guò)會(huì)在權(quán)限和窗口顯示次序等方面會(huì)有些不同。但是在WindowManager處理部分會(huì)有所不同诚镰,這里以最典型的應(yīng)用程序窗口Activity為例奕坟,Activity在啟動(dòng)過(guò)程中,如果Activity所在的進(jìn)程不存在則會(huì)創(chuàng)建新的進(jìn)程清笨,創(chuàng)建新的進(jìn)程之后就會(huì)運(yùn)行代表主線程的實(shí)例ActivityThread月杉,不了解的請(qǐng)查看Android應(yīng)用程序進(jìn)程啟動(dòng)過(guò)程(前篇)這篇文章。ActivityThread管理著當(dāng)前應(yīng)用程序進(jìn)程的線程抠艾,這在Activity的啟動(dòng)過(guò)程中運(yùn)用的很明顯沙合,不了解的請(qǐng)查看Android深入四大組件(一)應(yīng)用程序啟動(dòng)過(guò)程(后篇)這篇文章。當(dāng)界面要與用戶進(jìn)行交互時(shí)跌帐,會(huì)調(diào)用ActivityThread的handleResumeActivity方法首懈,如下所示。

frameworks/base/core/java/android/app/ActivityThread.java

 final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
       ...   
         r = performResumeActivity(token, clearHide, reason);//1           
  ...
 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();//2
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);//3
                }
...                
}

注釋1處的performResumeActivity方法最終會(huì)調(diào)用Activity的onResume方法谨敛。在注釋2處得到ViewManager類型的wm對(duì)象究履,在注釋3處調(diào)用了wm的addView方法,而addView方法的實(shí)現(xiàn)則是在WindowManagerImpl中脸狸,此后的過(guò)程在上面的系統(tǒng)窗口的添加過(guò)程已經(jīng)講過(guò)最仑,唯一需要注意的是addView的第一個(gè)參數(shù)是DecorView。

結(jié)語(yǔ)

ViewManager不只定義了addView方法用來(lái)添加窗口炊甲,還定義了updateViewLayout和removeView方法用來(lái)更新和刪除窗口泥彤,如下所示。

package android.view;
public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

其定義的updateViewLayout和removeView方法的處理流程和addView方法是類似的卿啡,都是要經(jīng)過(guò)WindowManagerGlobal處理吟吝,最后通過(guò)Session與WMS進(jìn)行跨進(jìn)程通信,將更新和刪除窗口的工作交由WMS來(lái)處理颈娜,這里不會(huì)對(duì)其進(jìn)行介紹剑逃,想了解可以查看源碼或者查看《Android開發(fā)藝術(shù)探索》第八章。

參考資料
《深入理解Android內(nèi)核設(shè)計(jì)思想》第二版
《深入理解Android:卷III》
《Android開發(fā)藝術(shù)探索》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末官辽,一起剝皮案震驚了整個(gè)濱河市蛹磺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌同仆,老刑警劉巖萤捆,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異俗批,居然都是意外死亡俗或,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門扶镀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蕴侣,“玉大人,你說(shuō)我怎么就攤上這事臭觉±ト福” “怎么了?”我有些...
    開封第一講書人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵蝠筑,是天一觀的道長(zhǎng)狞膘。 經(jīng)常有香客問(wèn)我,道長(zhǎng)什乙,這世上最難降的妖魔是什么挽封? 我笑而不...
    開封第一講書人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮臣镣,結(jié)果婚禮上辅愿,老公的妹妹穿的比我還像新娘智亮。我一直安慰自己,他們只是感情好点待,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開白布阔蛉。 她就那樣靜靜地躺著,像睡著了一般癞埠。 火紅的嫁衣襯著肌膚如雪状原。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,806評(píng)論 1 290
  • 那天苗踪,我揣著相機(jī)與錄音颠区,去河邊找鬼。 笑死通铲,一個(gè)胖子當(dāng)著我的面吹牛毕莱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播测暗,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼央串,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了碗啄?” 一聲冷哼從身側(cè)響起质和,我...
    開封第一講書人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎稚字,沒(méi)想到半個(gè)月后饲宿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胆描,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年瘫想,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昌讲。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡国夜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出短绸,到底是詐尸還是另有隱情车吹,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布醋闭,位于F島的核電站窄驹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏证逻。R本人自食惡果不足惜乐埠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丈咐,春花似錦瑞眼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至歹河,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間花吟,已是汗流浹背秸歧。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留衅澈,地道東北人键菱。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像今布,于是被迫代替她去往敵國(guó)和親经备。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

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