一、前言
在 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/