Framework 源碼解析知識梳理(2) - 應(yīng)用進程與 WMS 的通信實現(xiàn)

一腌巾、前言

Framework 源碼解析知識梳理(1) - 應(yīng)用進程與 AMS 的通信實現(xiàn) 這篇文章中鲫竞,我們分析了應(yīng)用進程和AMS之間的通信實現(xiàn)辐怕,我們今天討論一下應(yīng)用進程和WindowManagerService之間的通信實現(xiàn)。

在之前的分析中从绘,我們分兩個部分來介紹了應(yīng)用進程與AMS之間的通信:

  • 應(yīng)用進程發(fā)送消息到AMS進程
  • AMS發(fā)送消息到應(yīng)用進程

現(xiàn)在寄疏,我們也按照一樣的討論,分為這兩個方向來介紹應(yīng)用進程與WMS之間的通信實現(xiàn)僵井,整個通信的過程會涉及到下面的這些類陕截,其中加粗的線就是整個通信實現(xiàn)的調(diào)用路徑。

二批什、WindowManagerImpl & WindowManagerGlobal

AMS的討論中农曲,我們以在應(yīng)用進程中啟動Activity為例子進行了介紹,今天驻债,我們選取另一個大家很常見的例子:Activity啟動之后乳规,是如何將界面添加到屏幕上的。

2.1 WindowManagerImpl

View 繪制體系知識梳理(2) - setContentView 源碼解析 這篇文章中合呐,我們介紹了DecorView的相關(guān)知識暮的,它就對應(yīng)于我們需要添加到屏幕上的View的根節(jié)點,而這一添加的過程就需要涉及到和WMS之間的通信淌实,它是在ActivityThread的下面這個方法中實現(xiàn)的:

<!-- ActivityThread.java -->

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        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;
                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);
                }
            } else if (!willBeVisible) {
                if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }
        }
    }

上面的代碼中冻辩,關(guān)鍵的是下面這幾個步驟:

//(1) 通過 Activity 獲得 Window 的實現(xiàn)類 PhoneWindow
r.window = r.activity.getWindow();

//(2) 通過 PhoneWindow 獲得 DecorView
View decor = r.window.getDecorView();

//(3) 通過 Activity 獲得 ViewManager 的實現(xiàn)類 WindowManagerImpl
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();

//(4) 通過 WindowManagerImpl 添加 DecorView
wm.addView(decor, l);

(1) 通過 Activity 獲得 Window 的實現(xiàn)類 PhoneWindow

這里首先調(diào)用了ActivitygetWindow()方法:

<!-- Activity.java -->

public Window getWindow() {
    return mWindow;
}

而這個mWindow是在Activity.attach(xxxx)中被賦值的猖腕,它其實是Window的實現(xiàn)類PhoneWindowPhoneWindow的構(gòu)造函數(shù)中傳入了Activity以及parentWindow

<!-- 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) {
        //....
        mWindow = new PhoneWindow(this, window);
}

第一步的分析就結(jié)束了恨闪,大家要記得一個結(jié)論:

通過ActivitygetWindow()返回的是PhoneWindow對象倘感,如果以后需要查看mWindow調(diào)用的函數(shù),那么應(yīng)當(dāng)首先去PhoneWindow.java中查看是否有對應(yīng)的實現(xiàn)咙咽,如果沒有侠仇,那么再去Window.java中尋找。

對應(yīng)于整個流程圖的中的這個部分:



**(2) 通過 PhoneWindow 獲得 DecorView **

在第二步中犁珠,通過第一步返回的PhoneWindow獲得DecorView逻炊,這個mDecor就是我們在 View 繪制體系知識梳理(2) - setContentView 源碼解析 所介紹的DecorView

<!-- PhoneWindow.java -->

@Override
public final View getDecorView() {
    if (mDecor == null || mForceDecorInstall) {
        installDecor();
    }
    return mDecor;
}

(3) 通過 Activity 獲得 ViewManager 的實現(xiàn)類 WindowManagerImpl

下面,我們看第三步犁享,這里通過ActivitygetWindowManager()返回了一個ViewManager的實現(xiàn)類:

<!-- Activity.java -->

public WindowManager getWindowManager() {
    return mWindowManager;
}

mWindow類似余素,它也是在Activityattach方法中賦值的:

<!-- 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) {
        //...
        mWindow = new PhoneWindow(this, window);
        //...
        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());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }

它通過WindowgetWindowManager()返回,我們看一下Window的這個方法:

<!-- Window.java -->

public WindowManager getWindowManager() {
    return mWindowManager;
}

PhoneWindowActivity類似炊昆,也有一個mWindowManager變量桨吊,我們再去看一下它被賦值的地方:

<!-- Activity.java -->

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);
        }
        mWindowManager = ((WindowManagerImpl) wm).createLocalWindowManager(this);
}

createLocalWindowManager返回的是一個WindowManagerImpl對象:

<!-- WindowManagerImpl.java -->

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

這樣第三步的結(jié)論就是:

通過ActivitygetWindowManager()方法返回的是它內(nèi)部的mWindowManager對象,而這個對象是通過Window中的mWindowManager得到的凤巨,它其實是ViewManager接口的實現(xiàn)類WindowManagerImpl视乐。

ViewManagerWindowManagerImpl的關(guān)系為:

(4) 通過 WindowManagerImpl 添加 DecorView

在第四步中,我們通過ViewManageraddView(View, WindowManager.LayoutParams)方法添加DecorView敢茁,經(jīng)過前面的分析佑淀,我們知道它其實是一個WindowManagerImpl對象,因此彰檬,我們?nèi)タ匆幌滤鶎崿F(xiàn)的addView方法:

public final class WindowManagerImpl implements WindowManager {

    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;

    private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }

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

可以看到WindowManagerImpl什么都沒有做伸刃,它只是一個代理類,真正去做工作的是mGlobal逢倍,并且這個mGlobal使用了單例模式捧颅,也就是說,同一個進程中的所有Activity较雕,調(diào)用的是同一個WindowManagerGlobal對象碉哑。

那么,我們下面分析的重點就集中在了WindowManagerGlobal上了亮蒋。

2.2 WindowManagerGlobal

我們來看WindowManagerGlobaladdView方法扣典,這里的第一個參數(shù)就是前面?zhèn)鬟f過來的mDecor

<!-- WindowManagerGlobal -->

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        //...
        ViewRootImpl root;
        View panelParentView = null;
        //....
        synchronized (mLock) {
            //...
            root = new ViewRootImpl(view.getContext(), display);
            mViews.add(view);
            mRoots.add(root);
        }
        try {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            throw e;
        }
    }

addView(xxx)方法中,會生成一個ViewRootImpl對象宛蚓,并調(diào)用它的setView(xxx)方法把它和DecorView和它關(guān)聯(lián)起來激捏,與WMS通信的邏輯都是由ViewRootImpl負責(zé)的,WindowManagerGlobal則負責(zé)用來管理應(yīng)用進程當(dāng)中的所有ViewRootImpl凄吏,對應(yīng)于整個框架圖的部分為:


在介紹ViewRootImpl之前远舅,我們還要先講一下在WindowManagerGlobal中比較重要的兩個靜態(tài)變量:

<!-- WindowManagerGlobal.java -->

private static IWindowManager sWindowManagerService;
private static IWindowSession sWindowSession;

(1) sWindowManagerService 為管理者進程在應(yīng)用進程中的代理對象

<!-- WindowManagerGlobal.java -->

sWindowManagerService = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));

(2) sWindowSession 為應(yīng)用進程和管理者進程之間的會話

<!-- WindowManagerGlobal.java -->

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

這個會話的方向為從應(yīng)用進程到管理者進程,通過這個會話痕钢,應(yīng)用進程就可以向管理者進程發(fā)送消息图柏,而發(fā)送消息的邏輯則是通過ViewRootImpl來實現(xiàn)的,下面我們就來看一下這個最重要的類是如何實現(xiàn)的任连。

三蚤吹、ViewRootImpl

ViewRootImpl實現(xiàn)了ViewParent接口


在這個類中有三個最關(guān)鍵的變量:

<!-- ViewRootImpl.java -->

View mView;
IWindowSession mWindowSession;
IWindow W;
  • mView(View):這就是我們在WindowManagerGlobal中通過setView()傳遞進來的,對應(yīng)于Activity中的DecorView随抠。
<!-- ViewRootImpl.java -->

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                //....
            }
        }
}
  • mWindowSession(IWindowSession):代表了從應(yīng)用進程到管理者進程的會話裁着,它其實就是WindowManagerGlobal中的sWindowSession,對于同一個進程拱她,會復(fù)用同一個會話二驰。
<!-- ViewRootImpl.java -->

public ViewRootImpl(Context context, Display display) {
    mWindowSession = WindowManagerGlobal.getWindowSession();
    //...
}
  • mWindow(W):代表了從管理者進程到應(yīng)用進程的會話,是在ViewRootImpl中定義的一個內(nèi)部類秉沼。
<!-- ViewRootImpl.java -->

    public ViewRootImpl(Context context, Display display) {
        mWindow = new W(this);
    }

    static class W extends IWindow.Stub {

        private final WeakReference<ViewRootImpl> mViewAncestor;
        private final IWindowSession mWindowSession;

        W(ViewRootImpl viewAncestor) {
            mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
            mWindowSession = viewAncestor.mWindowSession;
        }
    }

3.1 從應(yīng)用進程到管理者進程

IWindowSession是應(yīng)用進程到管理者進程的會話桶雀,它定義了管理者進程所支持的調(diào)用接口,通過IWindowSession內(nèi)部的管理者進程的遠程代理對象唬复,我們就可以實現(xiàn)從應(yīng)用進程向管理者進程發(fā)送消息魂拦。

而在管理者進程中威恼,通過WindowManagerService來處理來自各個應(yīng)用進程的消息,在WMS中有一個Session列表,所有從應(yīng)用進程到管理進程的會話都保存在該列表中抛杨。

<!-- WindowManagerService.java -->

final ArraySet<Session> mSessions = new ArraySet<>();

Session則實現(xiàn)了IWindowSession.Stub接口:


當(dāng)它收到消息之后,就會回調(diào)IWindowSession的接口方法袍镀。

我們舉一個例子倒堕,在ViewRootImplsetView(xxx)方法中,調(diào)用了IWindowSession的下面這個接口方法:

<!-- ViewRootImpl.java -->

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        //....
        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
            getHostVisibility(), mDisplay.getDisplayId(),
            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
            mAttachInfo.mOutsets, mInputChannel);
    }

最終這一跨進程的調(diào)用會回調(diào)到該應(yīng)用進程在管理者進程中對應(yīng)的Session對象的回調(diào)方法中:

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

3.2 從管理者進程到應(yīng)用進程

如果我們希望實現(xiàn)從管理者進程發(fā)送消息到應(yīng)用進程丰包,那么也需要一個應(yīng)用進程在管理者進程的代理對象禁熏。

在調(diào)用addToDisplay時,我們傳入的第一個參數(shù)是mWindow邑彪,前面我們介紹過瞧毙,它實現(xiàn)了IWindow.Stub接口:


這樣管理者進程在SessionaddToDisplay方法被回調(diào)時,就可以獲得一個遠程代理對象寄症,它就可以通過IWindow中定義的接口方法宙彪,實現(xiàn)從管理者進程到應(yīng)用進程的通信。

SessionaddToDisplay()方法中有巧,會調(diào)用WMSaddWindow方法释漆,而在addWindow方法中,它會創(chuàng)建一個WindowState對象篮迎,一個進程中的每個ViewRootImpl會對應(yīng)于一個IWindow會話男图,它們被保存在WMS的下面這個HashMap中:

<!-- WindowManagerService.java -->

final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();

其中key值就表示應(yīng)用進程在管理者進程中的遠程代理對象示姿,例如我們在WMS中調(diào)用了下面這個方法:

<!-- WindowState.java -->

mClient.windowFocusChanged(focused, inTouchMode);

那么應(yīng)用進程中IWindow.Stub的實現(xiàn)的ViewRootImpl.W類的對應(yīng)方法就會被回調(diào),在該回調(diào)方法中又會調(diào)用ViewRootImpl的方法:

<!-- ViewRootImpl.java -->

        @Override
        public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
            }
        }

而在ViewRootImplwindowFocusChanged方法中逊笆,會通過它內(nèi)部的一個ViewRootHandler發(fā)送消息栈戳,ViewRootHandlerLooper是和應(yīng)用進程中的主線程所綁定的,因此它就可以在handleMessage進行后續(xù)邏輯處理难裆。

<!-- ViewRootImpl.java -->

    final ViewRootHandler mHandler = new ViewRootHandler();

    public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
        Message msg = Message.obtain();
        msg.what = MSG_WINDOW_FOCUS_CHANGED;
        msg.arg1 = hasFocus ? 1 : 0;
        msg.arg2 = inTouchMode ? 1 : 0;
        mHandler.sendMessage(msg);
    }

四子檀、小結(jié)

做一個簡單的總結(jié),應(yīng)用進程與WMS之間通信是通過WindowManagerGlobalViewRootImpl來管理的乃戈,ViewRootImpl中的IWindowSession對應(yīng)于從應(yīng)用進程到WMS的通信褂痰,而IWindow對應(yīng)于從管理者進程到應(yīng)用進程的通信。


更多文章症虑,歡迎訪問我的 Android 知識梳理系列:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缩歪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子侦讨,更是在濱河造成了極大的恐慌驶冒,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件韵卤,死亡現(xiàn)場離奇詭異骗污,居然都是意外死亡,警方通過查閱死者的電腦和手機沈条,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門需忿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蜡歹,你說我怎么就攤上這事屋厘。” “怎么了月而?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵汗洒,是天一觀的道長。 經(jīng)常有香客問我父款,道長溢谤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任憨攒,我火速辦了婚禮世杀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肝集。我一直安慰自己瞻坝,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布杏瞻。 她就那樣靜靜地躺著所刀,像睡著了一般衙荐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上勉痴,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天赫模,我揣著相機與錄音树肃,去河邊找鬼蒸矛。 笑死,一個胖子當(dāng)著我的面吹牛胸嘴,可吹牛的內(nèi)容都是我干的雏掠。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼劣像,長吁一口氣:“原來是場噩夢啊……” “哼乡话!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起耳奕,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤绑青,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后屋群,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闸婴,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年芍躏,在試婚紗的時候發(fā)現(xiàn)自己被綠了邪乍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡对竣,死狀恐怖庇楞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情否纬,我是刑警寧澤吕晌,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站临燃,受9級特大地震影響睛驳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜谬俄,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一柏靶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧溃论,春花似錦屎蜓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辆苔。三九已至,卻和暖如春扼劈,著一層夾襖步出監(jiān)牢的瞬間驻啤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工荐吵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留骑冗,地道東北人。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓先煎,卻偏偏與公主長得像贼涩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子薯蝎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,860評論 2 361

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