天天看點

Framework 源碼解析知識梳理(2) 應用程序與 WMS 的通信實作一、前言二、WindowManagerImpl & WindowManagerGlobal三、ViewRootImpl四、小結

一、前言

在 Framework 源碼解析知識梳理(1) - 應用程序與 AMS 的通信實作 這篇文章中,我們分析了應用程序和

AMS

之間的通信實作,我們今天讨論一下應用程序和

WindowManagerService

之間的通信實作。

在之前的分析中,我們分兩個部分來介紹了應用程序與

AMS

之間的通信:

  • 應用程序發送消息到

    AMS

    程序
  • AMS

    發送消息到應用程序

現在,我們也按照一樣的讨論,分為這兩個方向來介紹應用程序與

WMS

之間的通信實作,整個通信的過程會涉及到下面的這些類,其中加粗的線就是整個通信實作的調用路徑。

二、WindowManagerImpl & WindowManagerGlobal

AMS

的讨論中,我們以在應用程序中啟動

Activity

為例子進行了介紹,今天,我們選取另一個大家很常見的例子:

Activity

啟動之後,是如何将界面添加到螢幕上的。

2.1 WindowManagerImpl

在 View 繪制體系知識梳理(2) - setContentView 源碼解析 這篇文章中,我們介紹了

DecorView

的相關知識,它就對應于我們需要添加到螢幕上的

View

的根節點,而這一添加的過程就需要涉及到和

WMS

之間的通信,它是在

ActivityThread

的下面這個方法中實作的:

<!-- 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 的實作類 PhoneWindow

這裡首先調用了

Activity

getWindow()

方法:

<!-- Activity.java -->

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

而這個

mWindow

是在

Activity.attach(xxxx)

中被指派的,它其實是

Window

的實作類

PhoneWindow

PhoneWindow

的構造函數中傳入了

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

第一步的分析就結束了,大家要記得一個結論:

通過

Activity

getWindow()

傳回的是

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

下面,我們看第三步,這裡通過

Activity

getWindowManager()

傳回了一個

ViewManager

的實作類:

<!-- Activity.java -->

public WindowManager getWindowManager() {
    return mWindowManager;
}
複制代碼
           

mWindow

類似,它也是在

Activity

attach

方法中指派的:

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

它通過

Window

getWindowManager()

傳回,我們看一下

Window

的這個方法:

<!-- Window.java -->

public WindowManager getWindowManager() {
    return mWindowManager;
}
複制代碼
           

PhoneWindow

Activity

類似,也有一個

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

這樣第三步的結論就是:

通過

Activity

getWindowManager()

方法傳回的是它内部的

mWindowManager

對象,而這個對象是通過

Window

中的

mWindowManager

得到的,它其實是

ViewManager

接口的實作類

WindowManagerImpl

ViewManager

WindowManagerImpl

的關系為:

(4) 通過 WindowManagerImpl 添加 DecorView

在第四步中,我們通過

ViewManager

addView(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

我們來看

WindowManagerGlobal

addView

方法,這裡的第一個參數就是前面傳遞過來的

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

來實作的,下面我們就來看一下這個最重要的類是如何實作的。

三、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

的接口方法。

我們舉一個例子,在

ViewRootImpl

setView(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

接口:

這樣管理者程序在

Session

addToDisplay

方法被回調時,就可以獲得一個遠端代理對象,它就可以通過

IWindow

中定義的接口方法,實作從管理者程序到應用程序的通信。

Session

addToDisplay()

方法中,會調用

WMS

addWindow

方法,而在

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

而在

ViewRootImpl

windowFocusChanged

方法中,會通過它内部的一個

ViewRootHandler

發送消息,

ViewRootHandler

Looper

是和應用程序中的主線程所綁定的,是以它就可以在

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

四、小結

做一個簡單的總結,應用程序與

WMS

之間通信是通過

WindowManagerGlobal

ViewRootImpl

來管理的,

ViewRootImpl

中的

IWindowSession

對應于從應用程序到

WMS

的通信,而

IWindow

對應于從管理者程序到應用程序的通信。

更多文章,歡迎通路我的 Android 知識梳理系列:

  • Android 知識梳理目錄:www.jianshu.com/p/fd82d1899…
  • 個人首頁:lizejun.cn
  • 個人知識總結目錄:lizejun.cn/categories/