【源码】Service或者Application的Context为什么不能用来启动Dialog

前言

我们一直都被告知不能使用非Activity外的Context去启动一个dialog,那到底是为什么呢?抱着这个疑问,我从源码里面找到了答案


分析

我们先写一个反面教材

		//这样写会闪退
		Dialog dialog = new Dialog(Application.getInstance);
        dialog.show();

一、错误信息

在这里插入图片描述

根据Log我们可以知道,错误是从ViewRootImplsetView里面发出的,我们开始一步一步定位错误


二、定位错误


    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
   
        synchronized (this) {
   
            if (mView == null) {
   
                mView = view;
				...
                requestLayout();
                ...
                try {
   
                    ...
                    //这是一次IPC
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
                    setFrame(mTmpFrame);
                }
                ...

				//错误就在这里
                if (res < WindowManagerGlobal.ADD_OKAY) {
   
                	...
                    switch (res) {
   
                        case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
                        case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- token " + attrs.token
                                    + " is not valid; is your activity running?");
                        
                        ...
                    }
                    throw new RuntimeException(
                            "Unable to add window -- unknown error code " + res);
                }
			...
            }
        }
    }

可以看到我们的错误是在:

  • res < WindowManagerGlobal.ADD_OKAY
  • case WindowManagerGlobal.ADD_BAD_APP_TOKEN || case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN

这两个条件同时满足时出现的。

首先ADD_OKAY是定义在WindowManagerGlobal中的,除此之外还有其它的常量,而res是从WMS那里被赋值的(mWindowSession.addToDisplay最终调用了WMS.addWindow,这个可以通过XRef查源码,这里不再赘述)。根据这里的判断逻辑,也就是说我们只要在WMS.addWindow里面找到这样的代码块:return出以下定义中除ADD_OKAY外的任意一个值,就能定位到具体错误所在地方

    public static final int ADD_OKAY = 0;
    public static final int ADD_BAD_APP_TOKEN = -1;
    public static final int ADD_BAD_SUBWINDOW_TOKEN = -2;
    public static final int ADD_NOT_APP_TOKEN = -3;
    public static final int ADD_APP_EXITING = -4;
    public static final int ADD_DUPLICATE_ADD = -5;
    public static final int ADD_STARTING_NOT_NEEDED = -6;
    public static final int ADD_MULTIPLE_SINGLETON = -7;
    public static final int ADD_PERMISSION_DENIED = -8;
    public static final int ADD_INVALID_DISPLAY = -9;
    public static final int ADD_INVALID_TYPE = -10;

然后进入WMS.addWindow我们可以发现每个被WMS判断为不合法的情况,下面都会带有一句Slog.w(TAG_WM...)。因此我们可以通过运行触发错误,然后将Log等级设置为warn以上,就可以看到会有这样一句Log
(注:这里的tagWindowManager,因为我从Xref找到关于TAG_WM的定义如下第二张图)

在这里插入图片描述
在这里插入图片描述

有了具体的错误Log我们就能在WMS里面直接搜索了,在WMS.addWindow中通过Log定位到代码位置如下:


    public int addWindow(Session session, IWindow client, int seq
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值