在 Framework 源碼解析知識梳理(1) - 應用進程與 AMS 的通訊實現 這篇文章中,咱們分析了應用進程和AMS
之間的通訊實現,咱們今天討論一下應用進程和WindowManagerService
之間的通訊實現。java
在以前的分析中,咱們分兩個部分來介紹了應用進程與AMS
之間的通訊:bash
AMS
進程AMS
發送消息到應用進程如今,咱們也按照同樣的討論,分爲這兩個方向來介紹應用進程與WMS
之間的通訊實現,整個通訊的過程會涉及到下面的這些類,其中加粗的線就是整個通訊實現的調用路徑。 app
在AMS
的討論中,咱們以在應用進程中啓動Activity
爲例子進行了介紹,今天,咱們選取另外一個你們很常見的例子:Activity
啓動以後,是如何將界面添加到屏幕上的。框架
在 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
這裏首先調用了Activity
的getWindow()
方法:ui
<!-- Activity.java -->
public Window getWindow() {
return mWindow;
}
複製代碼
而這個mWindow
是在Activity.attach(xxxx)
中被賦值的,它實際上是Window
的實現類PhoneWindow
,PhoneWindow
的構造函數中傳入了Activity
以及parentWindow
:this
<!-- 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
經過
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
上了。
咱們來看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
實現了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;
}
}
複製代碼
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);
}
複製代碼
若是咱們但願實現從管理者進程發送消息到應用進程,那麼也須要一個應用進程在管理者進程的代理對象。
在調用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
對應於從管理者進程到應用進程的通訊。