Android P WindowManager (三) window添加時主要參數的分析(2) client(IWindow)。

在WindowManagerService.addWindow每次的client(IWindow)是不同的,而client是什麼呢?這個要從這個client在應用的創建開始了

(1)應用端client的初始化和傳遞

frameworks/base/core/java/android/view/ViewRootImpl.java

    //在ViewRootImpl的構造器中創建了client在應用端的實現mWindow
    public ViewRootImpl(Context context, Display display) {
        /*......*/
        mWindow = new W(this);
        /*......*/
    }
    //在ViewRootImpl.setView中調用到了session傳給WindowManagerService去處理
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                    /*......*/
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                    /*......*/
    }

    //client在應用中的是一個AIDL的服務端實現,那就說明WindowManagerService端的話就是一個客戶端的實現了.
    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;
        }
        /*......*/
    }

client在應用端的創建可以看到,應用端的client就是一個IWindow.Stub的實現,依附與ViewRootImpl,通過session.addToDisplay傳遞給服務端要在服務端生成一個AIDL的客戶端,也就是說在WindowManagerService,它就是一個client.

(2)服務端的client(IWindow)

frameworks/base/base/services/core/java/com/android/server/wm/WindowManagerService.java

    //在addWindow中吧這個client添加到mWindowMap中
    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
            /*......*/
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
            /*......*/
            mWindowMap.put(client.asBinder(), win);
            /*......*/
    }

/*************************************************/下面是client的幾個作用我用萬能分割符分開下
mWindowMap是WindowHashMap類型的變量,WindowHashMap繼承了HashMap,它限制了HashMap的key值的類型爲IBinder,value值的類型爲WindowState。也就是說在WindowManagerService中保存Window信息的存儲器了.而且也可以發現的是client就是WindowManagerService區分Window的key,只要知道了client那麼對應的Window也就能獲取到了.
/*************************************************/
另外在上面的代碼中WindowState初始化時也用到了client,這裏就真正用到client的AIDL的作用了,通過WindowState獲取client,然後回調應用端的"class W extends IWindow.Stub".

frameworks/base/base/services/core/java/com/android/server/wm/WindowState.java

    final IWindow mClient;
    private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
            Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
            MergedConfiguration mergedConfiguration, boolean reportOrientation, int displayId,
            DisplayCutout displayCutout)
            throws RemoteException {
        final boolean forceRelayout = isDragResizeChanged() || reportOrientation;

        mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets,
                reportDraw, mergedConfiguration, getBackdropFrame(frame), forceRelayout,
                mPolicy.isNavBarForcedShownLw(this), displayId,
                new DisplayCutout.ParcelableWrapper(displayCutout));
        mDragResizingChangeReported = true;
    }

這裏很有意思的是IWindow.aidl是oneway模式,這是由於在WindowManagerService中我們拿到的是一個client的客戶端,client操作是會通過Binder調用到應用端的"class W extends IWindow.Stub",但是說到底WindowManagerService是一個管理者,管理者可以通過client這個喇叭通知應用端要做什麼,但不能等着應用端做完之後給我們回覆了我們再去通知下一個,否則的話工作的效率就太低了.

frameworks/base/core/java/android/view/IWindow.aidl

/**
 * API back to a client window that the Window Manager uses to inform it of
 * interesting things happening.
 *
 * {@hide}
 */
oneway interface IWindow {
}

還有一處有意思的是當服務端的client是一個"class W extends IWindow.Stub"時,說明這個時候添加的Window是由system_server創建的,因爲是同一個進程,所以client傳遞過程沒有經過Binder化的跨進程處理.例如在<<Android P WindowManager (二) window添加時主要參數的分析(1) WindowSession。>>中點擊設置應用啓動時,會先添加一個Window{ba4a949 u0 Splash Screen com.android.settings},而這個Window就是由system_server創建的.

02-27 11:45:04.600  2590  2691 V lishuo  : *** ADD session :Session{e5dc9ec 2590:1000}
02-27 11:45:04.600  2590  2691 V lishuo  : *** ADD client.asBinder :android.view.ViewRootImpl$W@f957350
02-27 11:45:04.600  2590  2691 V lishuo  : *** ADD windowState :Window{ba4a949 u0 Splash Screen com.android.settings}

由於服務端調用client時沒有辦法通過oneway模式,所以WindowManagerService就要對一些耗時的操作做了些處理.
frameworks/base/base/services/core/java/com/android/server/wm/WindowState.java

    final IWindow mClient;
    void reportResized() {
            /*......*/
            if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
                    && mClient instanceof IWindow.Stub) {
                // To prevent deadlock simulate one-way call if win.mClient is a local object.
                // 爲了防止死鎖,如果win.mclient是本地對象,則模擬單向調用。
                mService.mH.post(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            dispatchResized(frame, overscanInsets, contentInsets, visibleInsets,
                                    stableInsets, outsets, reportDraw, mergedConfiguration,
                                    reportOrientation, displayId, displayCutout);
                        } catch (RemoteException e) {
                            // Not a remote call, RemoteException won't be raised.
                        }
                    }
                });
            } else {
                dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
                        outsets, reportDraw, mergedConfiguration, reportOrientation, displayId,
                        displayCutout);
            }
            /*......*/
    }

/*************************************************/

client在WindowManagerService中的另外一個作用也在WindowManagerService.addWindow:

    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
            /*......*/
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
            if (token == null) {
                if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
                    Slog.w(TAG_WM, "Attempted to add application window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_INPUT_METHOD) {
                    Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_VOICE_INTERACTION) {
                    Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_WALLPAPER) {
                    Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_DREAM) {
                    Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_QS_DIALOG) {
                    Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
                    Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (type == TYPE_TOAST) {
                    // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
                    if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
                            parentWindow)) {
                        Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
                                + attrs.token + ".  Aborting.");
                        return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                    }
                }
                final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                token = new WindowToken(this, binder, type, false, displayContent,
                        session.mCanAddInternalSystemWindow);
            }else{
            /*......*/
    }

此處就用來作爲binder來生成WindowToken了,WindowToken就是用來同步AM和WM的操作了(AppWindowToken)等.這個地方可以參考下老羅的<<Android窗口管理服務WindowManagerService對窗口的組織方式分析>>,這個是講的很詳細的,後面我也儘量簡單的寫寫.

總結
client(IWindow)主要有以下幾點:

(1)每個Window有不同的client,所以client被WindowManagerService用作了識別Window的key.
(2)client(IWindow)就同WindowSession相反了,WindowSession是從應用指向了服務,而client是從服務指向了應用,處理一些從WindowManagerService發到應用的操作.
(3)對於一些特殊的Window,也被用來生成WindowToken,例如一些自定義的懸浮窗等.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章