美文网首页
Dialog 的构造方法的 context 必须传入 Activ

Dialog 的构造方法的 context 必须传入 Activ

作者: 馒Care | 来源:发表于2021-08-26 10:17 被阅读0次

    https://www.jianshu.com/p/f42fe33d99f9
    https://www.wanandroid.com/wenda/show/18281

    为什么传入一个非 Activity 的 context 会出现错误?

    • 首先看启动一个Activity的流程(Android10以及以上)

    ActivityThread-->performLaunchActivity-->Instrumentation-->ActivityTaskManager.getService().startActivity-->startActivityAsUser-->.execute()-->ActivityStarter.executeRequest-->new ActivityRecord()---->startActivityUnchecked--->mTargetStack.startActivityLocked--->ActivityStack(startActivityLocked)

    • ActivityRecord的父类是WindowToken

    WindowManagerService-->addWindow-->new WindowToken-->dc.addWindowToken(token, this)--->mTokenMap.put(binder, token);

    以上流程主要表面token的传递过程

    • Dialog的显示流程


      image.png
    WindowManagerService
    
    public int addWindow(Session session, IWindow client, int seq,
                LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
                Rect outContentInsets, Rect outStableInsets,
                DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
                InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
                int requestUserId) {
    WindowToken token = displayContent.getWindowToken(
                        hasParent ? parentWindow.mAttrs.token : attrs.token);
                // If this is a child window, we want to apply the same type checking rules as the
                // parent window type.
                final int rootType = hasParent ? parentWindow.mAttrs.type : type;
    
                boolean addToastWindowRequiresToken = false;
    
                if (token == null) {
    }
    }
    

    根据源码可知 displayContent.getWindowToken获取token,如果没有就显示异常。

    总结

    1.AMS启动Activity,会构建对应的ActivityRecord对象,实例化Token对象
    2.实例化的Token对象,最终会被存放在DisplayContent的mTokenMap集合中(也就是WMS的mTokenMap中)
    3.WMS在addWindow时,会根据当前window对象的Token进行校验
    4.由于Application的父类是ContextImpl,调用 getSystemService后,拿到的WindowManager是通过SystemServiceRegistry.getSystemService拿到的
    5.SystemServiceRegistry缓存了一份registerService,所以,当前的WM是没有token的,也就是说,application的context。不能直接被用来创建Dialog

    registerService(Context.WINDOW_SERVICE, WindowManager.class,
                    new CachedServiceFetcher<WindowManager>() {
                @Override
                public WindowManager createService(ContextImpl ctx) {
                    return new WindowManagerImpl(ctx);
                }});
    

    为什么非要一个Token?

    这是因为在WMS那边需要根据这个Token来确定Window的位置(不是说坐标),如果没有Token的话,就不知道这个窗口应该放到哪个容器上了

    传入的 context 一定要是 Activity 吗

    想要通过非Activity对象创建并正常显示Dialog,首先必须拥有SYSTEM_ALERT_WINDOW权限,还有,在调用Dialog.show方法之前,必须把Dialog的Window的type指定为SYSTEM_WINDOW类型,比如TYPE_SYSTEM_ALERT或TYPE_APPLICATION_OVERLAY。
    没有满足第一个条件的话,那肯定会报permission denied啦。
    如果在show之前没有指定Window的type为SYSTEM_WINDOW类型,一样会发生BadTokenException的,message是token null is not valid; is your activity running?。

    相关文章

      网友评论

          本文标题:Dialog 的构造方法的 context 必须传入 Activ

          本文链接:https://www.haomeiwen.com/subject/eeheiltx.html