Framework 源碼解析知識梳理(2) 應用進程與 WMS 的通訊實現

1、前言

Framework 源碼解析知識梳理(1) - 應用進程與 AMS 的通訊實現 這篇文章中,咱們分析了應用進程和AMS之間的通訊實現,咱們今天討論一下應用進程和WindowManagerService之間的通訊實現。java

在以前的分析中,咱們分兩個部分來介紹了應用進程與AMS之間的通訊:bash

  • 應用進程發送消息到AMS進程
  • AMS發送消息到應用進程

如今,咱們也按照同樣的討論,分爲這兩個方向來介紹應用進程與WMS之間的通訊實現,整個通訊的過程會涉及到下面的這些類,其中加粗的線就是整個通訊實現的調用路徑。 app

2、WindowManagerImpl & WindowManagerGlobal

AMS的討論中,咱們以在應用進程中啓動Activity爲例子進行了介紹,今天,咱們選取另外一個你們很常見的例子:Activity啓動以後,是如何將界面添加到屏幕上的。框架

2.1 WindowManagerImpl

View 繪製體系知識梳理(2) - setContentView 源碼解析 這篇文章中,咱們介紹了DecorView的相關知識,它就對應於咱們須要添加到屏幕上的View的根節點,而這一添加的過程就須要涉及到和WMS之間的通訊,它是在ActivityThread的下面這個方法中實現的:ide

<!-- 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;
            }
        }
    }
複製代碼

上面的代碼中,關鍵的是下面這幾個步驟:函數

//(1) 經過 Activity 得到 Window 的實現類 PhoneWindow
r.window = r.activity.getWindow();

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

//(3) 經過 Activity 得到 ViewManager 的實現類 WindowManagerImpl
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();

//(4) 經過 WindowManagerImpl 添加 DecorView
wm.addView(decor, l);
複製代碼

(1) 經過 Activity 得到 Window 的實現類 PhoneWindowoop

這裏首先調用了ActivitygetWindow()方法:ui

<!-- Activity.java -->

public Window getWindow() {
    return mWindow;
}
複製代碼

而這個mWindow是在Activity.attach(xxxx)中被賦值的,它實際上是Window的實現類PhoneWindowPhoneWindow的構造函數中傳入了Activity以及parentWindowthis

<!-- 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);
}
複製代碼

第一步的分析就結束了,你們要記得一個結論:spa

經過ActivitygetWindow()返回的是PhoneWindow對象,若是之後須要查看mWindow調用的函數,那麼應當首先去PhoneWindow.java中查看是否有對應的實現,若是沒有,那麼再去Window.java中尋找。

對應於整個流程圖的中的這個部分:

**(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 的實現類 WindowManagerImpl

下面,咱們看第三步,這裏經過ActivitygetWindowManager()返回了一個ViewManager的實現類:

<!-- 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);
}
複製代碼

這樣第三步的結論就是:

經過ActivitygetWindowManager()方法返回的是它內部的mWindowManager對象,而這個對象是經過Window中的mWindowManager獲得的,它實際上是ViewManager接口的實現類WindowManagerImpl

ViewManagerWindowManagerImpl的關係爲:

(4) 經過 WindowManagerImpl 添加 DecorView

在第四步中,咱們經過ViewManageraddView(View, WindowManager.LayoutParams)方法添加DecorView,通過前面的分析,咱們知道它實際上是一個WindowManagerImpl對象,所以,咱們去看一下它所實現的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,調用的是同一個WindowManagerGlobal對象。

那麼,咱們下面分析的重點就集中在了WindowManagerGlobal上了。

2.2 WindowManagerGlobal

咱們來看WindowManagerGlobaladdView方法,這裏的第一個參數就是前面傳遞過來的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對象,並調用它的setView(xxx)方法把它和DecorView和它關聯起來,與WMS通訊的邏輯都是由ViewRootImpl負責的,WindowManagerGlobal則負責用來管理應用進程當中的全部ViewRootImpl,對應於整個框架圖的部分爲:

在介紹 ViewRootImpl以前,咱們還要先講一下在 WindowManagerGlobal中比較重要的兩個靜態變量:

<!-- WindowManagerGlobal.java -->

private static IWindowManager sWindowManagerService;
private static IWindowSession sWindowSession;
複製代碼

(1) sWindowManagerService 爲管理者進程在應用進程中的代理對象

<!-- WindowManagerGlobal.java -->

sWindowManagerService = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
複製代碼

(2) sWindowSession 爲應用進程和管理者進程之間的會話

<!-- 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());
複製代碼

這個會話的方向爲從應用進程到管理者進程,經過這個會話,應用進程就能夠向管理者進程發送消息,而發送消息的邏輯則是經過ViewRootImpl來實現的,下面咱們就來看一下這個最重要的類是如何實現的。

3、ViewRootImpl

ViewRootImpl實現了ViewParent接口

在這個類中有三個最關鍵的變量:

<!-- ViewRootImpl.java -->

View mView;
IWindowSession mWindowSession;
IWindow W;
複製代碼
  • mView(View):這就是咱們在WindowManagerGlobal中經過setView()傳遞進來的,對應於Activity中的DecorView
<!-- ViewRootImpl.java -->

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                //....
            }
        }
}
複製代碼
  • mWindowSession(IWindowSession):表明了從應用進程到管理者進程的會話,它其實就是WindowManagerGlobal中的sWindowSession,對於同一個進程,會複用同一個會話。
<!-- ViewRootImpl.java -->

public ViewRootImpl(Context context, Display display) {
    mWindowSession = WindowManagerGlobal.getWindowSession();
    //...
}
複製代碼
  • mWindow(W):表明了從管理者進程到應用進程的會話,是在ViewRootImpl中定義的一個內部類。
<!-- 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 從應用進程到管理者進程

IWindowSession是應用進程到管理者進程的會話,它定義了管理者進程所支持的調用接口,經過IWindowSession內部的管理者進程的遠程代理對象,咱們就能夠實現從應用進程向管理者進程發送消息。

而在管理者進程中,經過WindowManagerService來處理來自各個應用進程的消息,在WMS中有一個Session列表,全部從應用進程到管理進程的會話都保存在該列表中。

<!-- WindowManagerService.java -->

final ArraySet<Session> mSessions = new ArraySet<>();
複製代碼

Session則實現了IWindowSession.Stub接口:

當它收到消息以後,就會回調 IWindowSession的接口方法。

咱們舉一個例子,在ViewRootImplsetView(xxx)方法中,調用了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);
    }
複製代碼

最終這一跨進程的調用會回調到該應用進程在管理者進程中對應的Session對象的回調方法中:

<!-- 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 從管理者進程到應用進程

若是咱們但願實現從管理者進程發送消息到應用進程,那麼也須要一個應用進程在管理者進程的代理對象。

在調用addToDisplay時,咱們傳入的第一個參數是mWindow,前面咱們介紹過,它實現了IWindow.Stub接口:

這樣管理者進程在 SessionaddToDisplay方法被回調時,就能夠得到一個遠程代理對象,它就能夠經過 IWindow中定義的接口方法,實現從管理者進程到應用進程的通訊。

SessionaddToDisplay()方法中,會調用WMSaddWindow方法,而在addWindow方法中,它會建立一個WindowState對象,一個進程中的每一個ViewRootImpl會對應於一個IWindow會話,它們被保存在WMS的下面這個HashMap中:

<!-- WindowManagerService.java -->

final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();
複製代碼

其中key值就表示應用進程在管理者進程中的遠程代理對象,例如咱們在WMS中調用了下面這個方法:

<!-- WindowState.java -->

mClient.windowFocusChanged(focused, inTouchMode);
複製代碼

那麼應用進程中IWindow.Stub的實現的ViewRootImpl.W類的對應方法就會被回調,在該回調方法中又會調用ViewRootImpl的方法:

<!-- ViewRootImpl.java -->

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

而在ViewRootImplwindowFocusChanged方法中,會經過它內部的一個ViewRootHandler發送消息,ViewRootHandlerLooper是和應用進程中的主線程所綁定的,所以它就能夠在handleMessage進行後續邏輯處理。

<!-- 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);
    }
複製代碼

4、小結

作一個簡單的總結,應用進程與WMS之間通訊是經過WindowManagerGlobalViewRootImpl來管理的,ViewRootImpl中的IWindowSession對應於從應用進程到WMS的通訊,而IWindow對應於從管理者進程到應用進程的通訊。


更多文章,歡迎訪問個人 Android 知識梳理系列:

相關文章
相關標籤/搜索