WindowManager#addView_2

158 阅读12分钟

看这篇文章前需要一点基础,可以通过我的这篇博客另外一篇来了解下。 如这里所说,addView方法分为两类,一个是ViewGroup类继承的addView方法;另一个人是WindowManager继承的addView方法。发现之前说的addView方法还需要补充,在WindowManager类继承的addView方法中,只看到了通过ViewRootImpl调用到WindowManagerService中的addWindow方法。 在学习完addWindow方法后,发现这里才是重点啊。这里记录下学习过程。

(1)、WindowManagerService概述:

WindowManagerService是SystemServer中的一个服务,主要功能是管控窗口的服务,在各个应用进程中都有自己的代理,WindowManager(也可以说WindowManagerGlobal), WindowManager和WindowManagerService之间的沟通则是通过IWindowSession的子类Session类完成,在ViewRootImpl类中会持有一个IWindowSession类型的对象mWindowSession。 这里也解释下WindowManagerService有多重要。在显示的流程中,一个Window就会对应一个Surface,就像PS里的图层一样,我可以调整每个图层的三维位置(垂直屏幕会有个z轴)和大小 ,那就得有一个管理者去管理这些,不然屏幕上的窗口就乱套了,都不知道那个窗口会被哪个窗口遮住。而且WMS还能控制窗口的动画和分配输入事件(暂时还没看到这)。 接下来介绍几个关键的成员变量: 1、 mPolicy:用来定义一些串口策略的规范,具体实现为PhoneWindowManager,在WMS创建时一同被创建; 2、 mSessions:于应用侧通信需要经过sessions,一个进程对应一个,类型为ArraySet; 3、 mFinishedStarting:元素类型为AppWindowToken,是WindowToken的子类,主要用来描述应用程序的windowToken结构,应用程序每个Activity都对应一个AppWindowToken,类型为ArrayList; 4、 mResizingWindows:元素为WindowState,用来存储正在调整大小的窗口,从名字就可以看出来和他类似的还有mPendingRemove、mDestroysurface和mDestroyPreservedSurface。类型是ArrayList; 5、 mAnimator:用于管理窗口的动画以及特效动画; 6、 mH:系统类的Handler,用于将任务入队; 7、 mInputManager:输入系统的管理者,会对触摸事件进行处理,会寻找合适的窗口处理输入事件

(2)、part1:checkAddPermission

进入addWindow方法后,首先会根据传入的参数:attrs.type、 isRoundedCornerOverlay,、attrs.packageName以及appOp进行权限检查,这里看到代码处对应的是

int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
        appOp);
//查询是否有添加窗口的权限
if (res != ADD_OKAY) {
    return res;
}

如上,调用mPolicy的checkAddPermission,mPolicy是WindowManagerPolicy类型,再看到这个类,发现WindowManagerPolicy是接口类,查看后发现只有PhoneWindowManager类实现了这个接口,所以查看方法的实现就需要到PhoneWindowManager类中查看,且因为只有res为ADD_OKAY才能添加窗口,故查看什么时候能添加窗口,而这里的条件也比较简单,基本是直接根据type来判断的。Type的分类如下: 1-99:应用窗口; 1000-1999:子窗口; 2000-2999:系统窗口

if (type < FIRST_SYSTEM_WINDOW || type > LAST_SYSTEM_WINDOW) {
    // 若type为非系统窗口可进行下一步
    return ADD_OKAY;
}
//接下来就是对系统窗口的分类
//如果是非alertWindow类型的系统窗口
if (!isSystemAlertWindowType(type)) {
    switch (type) {
        case TYPE_TOAST:
            outAppOp[0] = OP_TOAST_WINDOW;
            return ADD_OKAY;
        case TYPE_INPUT_METHOD:
        case TYPE_WALLPAPER:
        case TYPE_PRESENTATION:
        case TYPE_PRIVATE_PRESENTATION:
        case TYPE_VOICE_INTERACTION:
        case TYPE_ACCESSIBILITY_OVERLAY:
        case TYPE_QS_DIALOG:
        case TYPE_NAVIGATION_BAR_PANEL:
            // The window manager will check these.
            return ADD_OKAY;
    }
//处理非上述情况
    return (mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
            == PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
}
//接下来则是对alertWindow类型的系统窗口的处理
final int callingUid = Binder.getCallingUid();
// 如果调用addWindow的是系统应用 
if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
    return ADD_OKAY;
}
//下面的方法是在上面处理后的基础上,再通过appInfo来进行分类
//总结下就是当不是TYPE_APPLICATION_OVERLAY时,检查唤醒源或者自身的权限判断
if (appInfo == null || (type != TYPE_APPLICATION_OVERLAY && appInfo.targetSdkVersion >= O)) {
    return (mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
            == PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
}

if (mContext.checkCallingOrSelfPermission(SYSTEM_APPLICATION_OVERLAY)
        == PERMISSION_GRANTED) {
    return ADD_OKAY;
}
//下面是通过mode来判断。
final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, packageName,
        null /* featureId */, "check-add");
switch (mode) {
    case AppOpsManager.MODE_ALLOWED:
    case AppOpsManager.MODE_IGNORED:
        // 上面两类返回ADD_OKAY
        return ADD_OKAY;
    case AppOpsManager.MODE_ERRORED:
        // Don't crash legacy apps
        if (appInfo.targetSdkVersion < M) {
            return ADD_OKAY;
        }
        return ADD_PERMISSION_DENIED;
    default:
        // in the default mode, we will make a decision here based on
        // checkCallingPermission()
        return (mContext.checkCallingOrSelfPermission(SYSTEM_ALERT_WINDOW)
                == PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
}

然后这里再解释下alertWindow的类型:

public static boolean isSystemAlertWindowType(@WindowType int type) {
    switch (type) {
        case TYPE_PHONE:
        case TYPE_PRIORITY_PHONE:
        case TYPE_SYSTEM_ALERT:
        case TYPE_SYSTEM_ERROR:
        case TYPE_SYSTEM_OVERLAY:
        case TYPE_APPLICATION_OVERLAY:
            return true;
    }
    return false;
}

(3)、part2:处理display和type

if (!mDisplayReady) {//检查是否就绪
    throw new IllegalStateException("Display has not been initialialized");
}
//通过DisplayID获取displayContent
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);

if (displayContent == null) {
    ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does "
            + "not exist: %d. Aborting.", displayId);
    return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
//查询是否有权限访问该display
if (!displayContent.hasAccess(session.mUid)) {
    ProtoLog.w(WM_ERROR,
            "Attempted to add window to a display for which the application "
                    + "does not have access: %d.  Aborting.",
            displayContent.getDisplayId());
    return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
//查询是否已经添加过窗口,防止重复添加
if (mWindowMap.containsKey(client.asBinder())) {
    ProtoLog.w(WM_ERROR, "Window %s is already added", client);
    return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
//如果添加的窗口是子窗口的话,该窗口必须存在父窗口,且父窗口的类型不能是子窗口
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
    parentWindow = windowForClientLocked(null, attrs.token, false);
    if (parentWindow == null) {
        ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "
                + "%s.  Aborting.", attrs.token);
        return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
    }
    if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
            && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
        ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "
                + "%s.  Aborting.", attrs.token);
        return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
    }
}
//字面意思应该是是有类型窗口,其displayContent也必须是私有
if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
    ProtoLog.w(WM_ERROR,
            "Attempted to add private presentation window to a non-private display.  "
                    + "Aborting.");
    return WindowManagerGlobal.ADD_PERMISSION_DENIED;
}
//向非suitable 的Display添加TYPE_PRESENTATION
if (type == TYPE_PRESENTATION && !displayContent.getDisplay().isPublicPresentation()) {
    ProtoLog.w(WM_ERROR,
            "Attempted to add presentation window to a non-suitable display.  "
                    + "Aborting.");
    return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}

int userId = UserHandle.getUserId(session.mUid);
if (requestUserId != userId) {
    try {
        mAmInternal.handleIncomingUser(callingPid, callingUid, requestUserId,
                false /*allowAll*/, ALLOW_NON_FULL, null, null);
    } catch (Exception exp) {
        ProtoLog.w(WM_ERROR, "Trying to add window with invalid user=%d",
                requestUserId);
        return WindowManagerGlobal.ADD_INVALID_USER;
    }
    // It's fine to use this userId
    userId = requestUserId;
}

这里再插一句,屏幕上那么多窗口,得存储起来吧,那用什么数据类型呢?这里采用的是HashMap,定义如下,以IBinder为key,WindowState为value。

final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();

那又有小伙伴要问了,IBinder和WindowState又是什么? IBinder:是一个接口类,所以他是一个基类有两个类会继承自他分别是Binder和BinderProxy,这两个的区别就是一个是Server端的,一个是Client端的binder。然后WindowManagerService在SystemServer侧,应用向WMS发起请求是通过IWindowSession,那不能一直是应用找WMS啊,WMS万一也想找应用呢?所以他就得通过IWindow的代理来找,而这个代理类的asBinder()(就是返回一个代理了)就是这里的key。IWindow的定义在ViewRootImpl中,也就是说,WMS访问应用的窗口时通过访问ViewRootImpl达到目的的,而一个在应用侧addWindow的过程中(WindowManagerGlobal中的addView)会新建一个viewRootImpl,也就是说一个Window会对应一个viewRootImpl,所以这里的WMS就可以通过ViewRootImpl中IWindow的代理来标记这个WindowState。 WindowState:是window在WindowManagerService的存在形式,还记得之前在应用侧的WindowManager类中说的PhoneWindow嘛?PhoneWindow就是Window在应用侧的表现形式。WindowManagerService管理窗口的话,窗口得以一个实体化的载体呈现出来,WindowManagerService才能对其进行管理,就跟应用侧窗口就是PhoneWindow的形式展现。 所以直接的理解就是一个WindowState对应一个窗口。
所以WMS内,将Window通过其通信端进行分类,简单理解就是viewRootImpl(key)对应一个window(value)。

(4)、part3:创建新窗口的Token

ActivityRecord activity = null;
final boolean hasParent = parentWindow != null;
//获取窗口的Token,若为子窗口,可直接复用父窗口的Token,否则直接使用参数中的Token,这里很好理解,因为一个进程公用一个key,所以Token也是同一个
WindowToken token = displayContent.getWindowToken(
        hasParent ? parentWindow.mAttrs.token : attrs.token);
//是否为子窗口
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
//Toast窗口不需要令牌
boolean addToastWindowRequiresToken = false;

final IBinder windowContextToken = attrs.mWindowContextToken;
//如果没获取到Token
if (token == null) {
    if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,
            rootType, attrs.token, attrs.packageName)) {
        return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
    }
    //有一说一这里不是很懂,为啥前面没获取到这里就能获取?难道是因为前面是根据displaycontent这里是直接通过parentWindow? 
    if (hasParent) {
        // Use existing parent window token for child windows.
        token = parentWindow.mToken;
    } else if (mWindowContextListenerController.hasListener(windowContextToken)) {
        // 没有父窗口,自然是直接新建一个Token了.
        final IBinder binder = attrs.token != null ? attrs.token : windowContextToken;
        final Bundle options = mWindowContextListenerController
                .getOptions(windowContextToken);
        //这里就是直接去新建一个windowToken,参数啥的就不解读了
        token = new WindowToken.Builder(this, binder, type)
                .setDisplayContent(displayContent)
                .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                .setRoundedCornerOverlay(isRoundedCornerOverlay)
                .setFromClientToken(true)
                .setOptions(options)
                .build();
    } else {
        final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
        token = new WindowToken.Builder(this, binder, type)
                .setDisplayContent(displayContent)
                .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                .setRoundedCornerOverlay(isRoundedCornerOverlay)
                .build();
    }
//下面就是对一些特殊的窗口的token进行检查,如果异常直接终止添加窗口流程
} else if (rootType >= FIRST_APPLICATION_WINDOW
        && rootType <= LAST_APPLICATION_WINDOW) {
    //如果是应用窗口的话,通过token获取ActivityRecord
    activity = token.asActivityRecord();
    if (activity == null) {
        ProtoLog.w(WM_ERROR, "Attempted to add window with non-application token "
                + ".%s Aborting.", token);
        return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
    } else if (activity.getParent() == null) {
        ProtoLog.w(WM_ERROR, "Attempted to add window with exiting application token "
                + ".%s Aborting.", token);
        return WindowManagerGlobal.ADD_APP_EXITING;
    } else if (type == TYPE_APPLICATION_STARTING) {//这里是启动窗口
        if (activity.mStartingWindow != null) {//启动窗口已经添加到应用了
            ProtoLog.w(WM_ERROR, "Attempted to add starting window to "
                    + "token with already existing starting window");
            return WindowManagerGlobal.ADD_DUPLICATE_ADD;
        }
        if (activity.mStartingData == null) {
            ProtoLog.w(WM_ERROR, "Attempted to add starting window to "
                    + "token but already cleaned");
            return WindowManagerGlobal.ADD_DUPLICATE_ADD;
        }
    }
} else if (rootType == TYPE_INPUT_METHOD) {
    //如果是输入法窗口
    if (token.windowType != TYPE_INPUT_METHOD) {
        ProtoLog.w(WM_ERROR, "Attempted to add input method window with bad token "
                + "%s.  Aborting.", attrs.token);
        return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
    }
} else if (rootType == TYPE_VOICE_INTERACTION) {
    //不知道啥类型,总之下面就是token和rootType类型不同
    if (token.windowType != TYPE_VOICE_INTERACTION) {
        ProtoLog.w(WM_ERROR, "Attempted to add voice interaction window with bad token "
                + "%s.  Aborting.", attrs.token);
        return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
    }
} else if (rootType == TYPE_WALLPAPER) {
    //壁纸窗口
    if (token.windowType != TYPE_WALLPAPER) {
        ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with bad token "
                + "%s.  Aborting.", attrs.token);
        return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
    }
} else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
    if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
        ProtoLog.w(WM_ERROR,
                "Attempted to add Accessibility overlay window with bad token "
                        + -"%s.  Aborting.", attrs.token);
        return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
    }
} else if (type == TYPE_TOAST) {//toast窗口
    addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
            callingUid, parentWindow);
    if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
        ProtoLog.w(WM_ERROR, "Attempted to add a toast window with bad token "
                + "%s.  Aborting.", attrs.token);
        return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
    }
} else if (type == TYPE_QS_DIALOG) {
    if (token.windowType != TYPE_QS_DIALOG) {
        ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with bad token "
                + "%s.  Aborting.", attrs.token);
        return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
    }
} else if (token.asActivityRecord() != null) {
    ProtoLog.w(WM_ERROR, "Non-null activity for system window of rootType=%d",
            rootType);
    attrs.token = null;
    token = new WindowToken.Builder(this, client.asBinder(), type)
            .setDisplayContent(displayContent)
            .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
            .build();
}

WMS是一个大的服务,所有应用都来找他添加窗口,那总不能谁都有权限吧,所以就引入了WindowToken作为一个权限管理手段,WindowToken是一个群组,这个群组里放了一类WindowState,他们具有同样的权限,其实简单理解就是一个进程公用一个token。

(5)、part4:新建一个WindowState:

做了这么多检查,终于可以新建一个window了,于是直接通过传入的参数创建一个新的Windowstate。这样WMS就可以通过操作Windowstate的属性来改变窗口里的内容了。

final WindowState win = new WindowState(this, session, client, token, parentWindow,
        appOp[0], attrs, viewVisibility, session.mUid, userId,
        session.mCanAddInternalSystemWindow);
if (win.mDeathRecipient == null) {
    // 请求的客户端挂了,那不就直接退出了
    ProtoLog.w(WM_ERROR, "Adding window client %s"
            + " that is dead, aborting.", client.asBinder());
    return WindowManagerGlobal.ADD_APP_EXITING;
}

if (win.getDisplayContent() == null) {
    //如果这个Window分配的地方已经被移除了,那也不知道往哪添加不是
    ProtoLog.w(WM_ERROR, "Adding window to Display that has been removed.");
    return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}

final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
//调整窗口的参数,确保部分窗口不能获取输入事件
displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid);
attrs.inputFeatures = sanitizeSpyWindow(attrs.inputFeatures, win.getName(), callingUid,
        callingPid);
win.setRequestedVisibilities(requestedVisibilities);

// 检查窗口是否能被添加到系统. 如对于某一个Display,只能添加一个StatusBar
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
if (res != ADD_OKAY) {
    return res;
}
//判断是否需要创建InputChannel
final boolean openInputChannels = (outInputChannel != null
        && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if  (openInputChannels) {//开始监听input事件
    win.openInputChannel(outInputChannel);
}
if (type == TYPE_TOAST) {
    if (!displayContent.canAddToastWindowForUid(callingUid)) {
        //根据UID检查是否能够添加Toast
        ProtoLog.w(WM_ERROR, "Adding more than one toast window for UID at a time.");
        return WindowManagerGlobal.ADD_DUPLICATE_ADD;
    }
    if (addToastWindowRequiresToken
            || (attrs.flags & FLAG_NOT_FOCUSABLE) == 0
            || displayContent.mCurrentFocus == null
            || displayContent.mCurrentFocus.mOwnerUid != callingUid) {
        mH.sendMessageDelayed(
                mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
                win.mAttrs.hideTimeoutMilliseconds);
    }
}
//不知道监听啥,反正不能让子窗口听
if (!win.isChildWindow()
        && mWindowContextListenerController.hasListener(windowContextToken)) {
    final int windowContextType = mWindowContextListenerController
            .getWindowType(windowContextToken);
    final Bundle options = mWindowContextListenerController
            .getOptions(windowContextToken);
    if (type != windowContextType) {
        ProtoLog.w(WM_ERROR, "Window types in WindowContext and"
                + " LayoutParams.type should match! Type from LayoutParams is %d,"
                + " but type from WindowContext is %d", type, windowContextType);
        // We allow WindowProviderService to add window other than windowContextType,
        // but the WindowProviderService won't be associated with the window's
        // WindowToken.
        if (!isWindowProviderService(options)) {
            return WindowManagerGlobal.ADD_INVALID_TYPE;
        }
    } else {
        mWindowContextListenerController.registerWindowContainerListener(
                windowContextToken, token, callingUid, type, options);
    }
}
//从这里开始,就肯定能添加窗口了。
res = ADD_OKAY;
//是否需要使用BLAST,这应该是某项绘制功能
if (mUseBLAST) {
    res |= WindowManagerGlobal.ADD_FLAG_USE_BLAST;
}
// dc的focus窗口为null, 将当前win添加到mWinAddedSinceNullFocus
if (displayContent.mCurrentFocus == null) {
    displayContent.mWinAddedSinceNullFocus.add(win);
}
//将一些窗口归类到mTapExcludeWindows里面
if (excludeWindowTypeFromTapOutTask(type)) {
    displayContent.mTapExcludedWindows.add(win);
}
//向windowmap中添加 通过addWindow方法参数创建出来的window
win.attach();
//将新建出来的window放入map中
mWindowMap.put(client.asBinder(), win);
//处理APP的op状态
win.initAppOpsState();
//获取一些状态量
final boolean suspended = mPmInternal.isPackageSuspended(win.getOwningPackage(),
        UserHandle.getUserId(win.getOwningUid()));
win.setHiddenWhileSuspended(suspended);

final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);

boolean imMayMove = true;
//将窗口放入属于他的群组中
win.mToken.addWindow(win);
displayPolicy.addWindowLw(win, attrs);
displayPolicy.setDropInputModePolicy(win, win.mAttrs);
//下面针对不同类型的窗口再进行一些设置
//如果是启动窗口的话,将窗口和对应的Activity关联
if (type == TYPE_APPLICATION_STARTING && activity != null) {
    activity.attachStartingWindow(win);
    ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
            activity, win);
} else if (type == TYPE_INPUT_METHOD
        //对输入法窗口进行设置
        && (win.getAttrs().flags & FLAG_NOT_TOUCHABLE) == 0) {
    displayContent.setInputMethodWindowLocked(win);
    imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
    displayContent.computeImeTarget(true /* updateImeTarget */);
    imMayMove = false;
} else {
    if (type == TYPE_WALLPAPER) {
        displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
        displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
    } else if (win.hasWallpaper()) {
        displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
    } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
        displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
    }
}
//后面的一些设置暂时还没看明白,后面再说吧

(6)、总结:

到这,我们完成了再应用侧,向WMS发送添加窗口的申请,并成功的添加了窗口。其实整个过程很简单,1、就是检查权限;2、根据请求的客户端type进行一些预处理;3、 获取其WindowToken;4、新建WindowState,并加入WMS之中; 在这之中我们了解了应用侧和WMS侧的关联,因为他们处于两个进程,所以需要通过binder进行通信,这之中就关系到他们通信的方式:应用侧通过Session向WMS发送消息,Session在应用侧是代理的形式,其真身是定义在WMS;当然通信就得是双向的,WMS也要向应用侧发送请求,那就得通过IWindow进行通信,在WMS中IWindow都是代理的形式,其真身定义在了APP侧(ViewRootImpl中)。 这里就很好类比,就像你用微信找朋友帮个忙,那他本人不在你身边,你怎么告诉他呢? 我们可以发微信,打电话对吧,而微信和通讯录里的他的联系方式不是他本人,只是他本人的一个代理,你可以通过这个代理访问到他,这点理解起来也不难,你朋友是个人,所以他不能存在于微信中和通讯录中,这里的他只是他的联系方式,也就是代理,你打电话给他要打他的电话号码,那他找你也得有你的电话号码吧,所以这里的代理就可以这么理解了。 在这里插入图片描述