轉載地址:https://blog.csdn.net/yangwen123/article/details/18733631
我們知道WindowManagerService服務運行在SystemServer進程中,應用程序啓動Activity時,需要請求WMS爲啓動的Activity創建對應的窗口,同時WMS也負責修改窗口屬性,因此這裏就涉及到應用程序進程與WMS服務之間的跨進程交互過程。在前面我們介紹了Android中的Binder通信機制,應用程序進程正是使用Binder通信方式和SystemServer進程交互的。
在應用程序進程中啓動的每一個Activity都擁有一個ViewRootImpl對象:
frameworks\base\core\java\android\view\ViewRootImpl.java
- public ViewRootImpl(Context context, Display display) {
- //在WMS服務中創建Session Binder對象,同時返回Binder代理對象給當前應用程序進程,並保存在ViewRootImpl的成員變量mWindowSession中
- mWindowSession = WindowManagerGlobal.getWindowSession();
- ...
- //爲每一個ViewRootImpl對象創建W Binder對象,用於WMS服務訪問對應的Activity
- mWindow = new W(this);
- ...
- mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
- ...
- mChoreographer = Choreographer.getInstance();
- ...
- loadSystemProperties();
- }
1.建立應用程序到WMS服務之間的連接
- public static IWindowSession getWindowSession() {
- synchronized (WindowManagerGlobal.class) {
- if (sWindowSession == null) {
- try {
- InputMethodManager imm = InputMethodManager.getInstance();
- //得到WindowManagerService服務的代理對象
- IWindowManager windowManager = getWindowManagerService();
- //通過WindowManagerService服務代理對象請求在WMS中創建Session本地對象
- sWindowSession = windowManager.openSession(imm.getClient(), imm.getInputContext());
- float animatorScale = windowManager.getAnimationScale(2);
- ValueAnimator.setDurationScale(animatorScale);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to open window session", e);
- }
- }
- return sWindowSession;
- }
- }
函數首先通過getWindowManagerService來獲取WMS服務的代理對象,由於WMS服務是一個有名Binder對象,即已經註冊到了ServiceManager進程中,因此可以通過查詢服務的方式來獲取WMS的代理對象。
- public static IWindowManager getWindowManagerService() {
- synchronized (WindowManagerGlobal.class) {
- if (sWindowManagerService == null) {
- sWindowManagerService = IWindowManager.Stub.asInterface(
- ServiceManager.getService("window"));
- }
- return sWindowManagerService;
- }
- }
得到了WMS服務的代理對象後,就可以通過該代理對象請求WMS服務創建一個Session Binder本地對象,該對象用於應用程序進程訪問WMS服務。關於應用程序進程使用WMS代理對象如何請求WMS創建Session的過程這裏不在介紹,這是Binder通信機制中函數遠程調用流程。上面通過調用WMS代理對象的openSession函數後,WMS服務的openSession函數實現如下:
- public IWindowSession openSession(IInputMethodClient client,
- IInputContext inputContext) {
- if (client == null) throw new IllegalArgumentException("null client");
- if (inputContext == null) throw new IllegalArgumentException("null inputContext");
- Session session = new Session(this, client, inputContext);
- return session;
- }
這裏直接構造一個Session對象,並將該對象返回給應用程序進程,這樣在應用程序這邊就可以得到Session的代理對象IWindowSession.Proxy,並保存在ViewRootImpl對象的成員變量mWindowSession中。2.建立WMS服務到應用程序進程之間的連接
- public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
- synchronized (this) {
- if (mView == null) {
- mView = view;
- ...
- //mWindow爲W本地對象
- res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
- getHostVisibility(), mDisplay.getDisplayId(),
- mAttachInfo.mContentInsets, mInputChannel);
- ...
- if (mInputChannel != null) {
- if (mInputQueueCallback != null) {
- mInputQueue = new InputQueue();
- mInputQueueCallback.onInputQueueCreated(mInputQueue);
- }
- mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
- Looper.myLooper());
- }
- ...
- }
- }
- }
在上一節中已經介紹了,ViewRootImpl對象的成員變量mWindowSession保存了Session的代理對象,向addToDisplay函數傳遞的第一個參數爲成員變量mWindow,而mWindow中保存了W類型的Binder本地對象,因此這裏通過函數addToDisplay就可以將W的代理對象傳遞給WMS服務。
- public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
- int viewVisibility, int displayId, Rect outContentInsets,
- InputChannel outInputChannel) {
- return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
- outContentInsets, outInputChannel);
- }
Session接收某一個應用程序進程的函數調用請求,並將請求轉交給WMS服務來完成。Session和WMS服務之間的關係如下:
- public int addWindow(Session session, IWindow client, int seq,
- WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
- Rect outContentInsets, InputChannel outInputChannel) {
- ...
- WindowToken token = mTokenMap.get(attrs.token);
- if (token == null) {
- ...
- token = new WindowToken(this, attrs.token, -1, false);
- addToken = true;
- //應用程序窗口
- } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
- AppWindowToken atoken = token.appWindowToken;
- ...
- //輸入法窗口
- } else if (type == TYPE_INPUT_METHOD) {
- ...
- //牆紙窗口
- } else if (type == TYPE_WALLPAPER) {
- ...
- } else if (type == TYPE_DREAM) {
- ...
- }
- //在WMS服務中爲窗口創建WindowState對象
- win = new WindowState(this, session, client, token,
- attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
- ...
- win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
- res = mPolicy.prepareAddWindowLw(win, attrs);
- ...
- if (outInputChannel != null && (attrs.inputFeatures
- & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
- String name = win.makeInputChannelName();
- InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
- win.setInputChannel(inputChannels[0]);
- inputChannels[1].transferTo(outInputChannel);
- mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
- }
- ...
- if (addToken) {
- //mTokenMap哈希表保存了所有的WindowToken對象
- mTokenMap.put(attrs.token, token);
- }
- win.attach();
- //mWindowMap哈希表以鍵值對的方式保存了所有的WindowState對象及W代理對象:<IWindow.Proxy,WindowState>
- mWindowMap.put(client.asBinder(), win);
- if (win.mAppOp != AppOpsManager.OP_NONE) {
- if (mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(), win.getOwningPackage())
- != AppOpsManager.MODE_ALLOWED) {
- win.setAppOpVisibilityLw(false);
- }
- }
- ...
- }
- ...
- }
該函數首先爲應用程序進程新增的窗口在WMS服務中創建對應的WindowState對象,並且將WMS,接收應用程序進程的Session,應用程序進程中的W代理對象保存到WindowState中。
- WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
- WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
- int viewVisibility, final DisplayContent displayContent) {
- mService = service;//標識WMS服務
- mSession = s;//標識接收應用程序進程的Session本地對象
- mClient = c;//標識應用程序進程中窗口的W代理對象
- mAppOp = appOp;
- mToken = token;//標識應用程序進程中的窗口布局參數的Token
- mOwnerUid = s.mUid;
- mWindowId = new IWindowId.Stub() {
- @Override
- public void registerFocusObserver(IWindowFocusObserver observer) {
- WindowState.this.registerFocusObserver(observer);
- }
- @Override
- public void unregisterFocusObserver(IWindowFocusObserver observer) {
- WindowState.this.unregisterFocusObserver(observer);
- }
- @Override
- public boolean isFocused() {
- return WindowState.this.isFocused();
- }
- };
- ...
- }
在構造WindowState對象過程中,又創建了一個用於標識指定窗口的IWindowId本地對象,因此應用程序進程必定會獲取該對象的代理對象。關於IWindowId代理對象的獲取過程在接下來分析。在WMS服務中爲應用程序進程中的指定窗口創建了對應的WindowState對象後,接着調用該對象的attach()函數:
- void attach() {
- if (WindowManagerService.localLOGV) Slog.v(
- TAG, "Attaching " + this + " token=" + mToken
- + ", list=" + mToken.windows);
- mSession.windowAddedLocked();
- }
在構造WindowState對象時,將用於接收應用程序進程請求的本地Session對象保存在WindowState的成員變量mSession中,這裏調用Session的windowAddedLocked()函數來創建請求SurfaceFlinger的SurfaceSession對象,同時將接收應用程序進程請求的Session保存到WMS服務的mSessions數組中。frameworks\base\services\java\com\android\server\wm\Session.java
- void windowAddedLocked() {
- if (mSurfaceSession == null) {
- mSurfaceSession = new SurfaceSession();
- mService.mSessions.add(this);
- }
- mNumWindow++;
- }
3. IWindowId代理對象獲取過程
- public WindowId getWindowId() {
- if (mAttachInfo == null) {
- return null;
- }
- if (mAttachInfo.mWindowId == null) {
- try {
- /**獲取WMS服務端的IWindowId代理對象,mAttachInfo在前面創建ViewRootImpl對象時創建
- * mWindow = new W(this);
- * mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
- * 在AttachInfo的構造函數中:
- * mWindow = window;
- * mWindowToken = window.asBinder();
- * 因此AttachInfo的成員變量mWindow和mWindowToken引用了同一對象,該對象就是類型爲W的本地binder對象
- * 下面通過W本地binder對象,來獲取AMS服務端的IWindowId的代理對象
- */
- mAttachInfo.mIWindowId = mAttachInfo.mSession.getWindowId(mAttachInfo.mWindowToken);
- WindowId類的定義主要是爲了方便在進程間傳輸IWindowId Binder對象
- mAttachInfo.mWindowId = new WindowId(mAttachInfo.mIWindowId);
- } catch (RemoteException e) {
- }
- }
- return mAttachInfo.mWindowId;
- }
應用程序進程還是通過Session的代理對象來獲取IWindowId的代理對象,參數window在應用程序進程端爲W本地binder對象,經過Binder傳輸到達WMS服務進程後,就轉換爲W的binder代理對象了。
- public IWindowId getWindowId(IBinder window) {
- return mService.getWindowId(window);
- }
在WMS服務中的Session本地對象又將IWindowId對象的查找過程交給WMS服務。
- public IWindowId getWindowId(IBinder token) {
- synchronized (mWindowMap) {
- //通過W的binder代理對象從mWindowMap哈希表中查找對應的WindowState對象。
- WindowState window = mWindowMap.get(token);
- return window != null ? window.mWindowId : null;
- }
- }
根據W的binder代理對象token在WMS中查找窗口對應的WindowState對象,再將該窗口在WindowState對象中創建的IWindowId Binder本地對象返回,這樣,客戶端進程就可以得到該Binder的代理對象。查找過程如下: