View not attached to window manager异常

QuibblerQuibbler 2020-06-11 约 11 分钟 2395 次阅读

View not attached to window manager异常

开发的越多,遇到的问题也就越多。知识的圈子不断扩大,接触到的问题也就越多。

1、异常

Activity退出后,又进行了Dialog的操作会抛出下面的异常,这个异常比较少见。但是确实暴露出代码逻辑的不严谨,有待强化。


2、分析原因

根据异常的堆栈,从源码开始看。跳过前面的各种调度,直接从Dialog开始:

public void show() {
        if (mShowing) {
            if (mDecor != null) {
                if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
                    mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
                }
                mDecor.setVisibility(View.VISIBLE);
            }
            return;
        }
        ...
        mWindowManager.addView(mDecor, l);
        ...
    }

mWindowManager是WindowManager类型,WindowManager是一个抽象的接口,真正的实现类是WindowManagerImpl,调用了其中的addView()方法(关于WindowManager参考《理解WindowManager》)。

@Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

mGlobal是WindowManagerGlobal类型对象,它的addView()方法如下:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            ...
            root = new ViewRootImpl(view.getContext(), display);
            ...
            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
    }

ViewRootImpl最终将Dialog中的View添加到视图中,通过setView()方法:

/**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                    ...
                    int res; /* = WindowManagerImpl.ADD_OKAY; */
                    ...
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
                    ...
                    if (res < WindowManagerGlobal.ADD_OKAY) {
                    mAttachInfo.mRootView = null;
                    mAdded = false;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    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?");
                ...
    }

中间省去大量的代码,这里判断Activity是否已经finish,如果添加给Dialog添加View依附的Activity已经结束,就抛出上面的异常。

3、解决办法

经过上面的分析,抛出这个问题的原因确实是和时序有一定的关系。

3.1、Activity退出时关闭

最好的做法就是在Activity退出的时候关闭依附于该Activity的Dialog。

@Override
    protected void onDestroy() {
        super.onDestroy();
        if (null != mDialog && mDialog.isShowing()) {
            mDialog.dismiss();
            mDialog = null;
        }
    }

并且有异步延时任务完成后也不再弹出Dialog,在需要弹出或者关闭Dialog的时候进行严谨的条件限制:

public void run() {
        ...
        //子线程完成任务后切回主线程
        //Dialog依附的Activity没有销毁,Dialog已初始化且没有弹出
        if (activity.isFinishing() && null != mDialog && !mDialog.isShowing()) {
            mDialog.show();
        }
        ...
    }

3.2、try-catch

关闭Dialog弹窗的时候判断条件,并进行可能的异常捕捉。不过不建议这种不负责任的做法,不是所有的异常try就行了,要归根结底从根源上解决。

try {
        mDialog.dismiss();
    } catch (Exception e) {
        
    }

参考资料:

        Android中View not attached to window manager错误的解决办法

        Android 报错处理:not attached to window manager

相关推荐

Honeycam 最好的GIF动图软件
精选分享

Honeycam 最好的GIF动图软件

Honeycam 最好的GIF动图软件推荐一款Gif制作软件:Honeycam,是一款高质量的GIF动图制作和编辑器。以前将视频转GIF,录制屏幕动图要费很大功夫,这款软件真的非常简洁好用,值得收藏,博客上的GIF都是用Honeycam录制的。官网:Honeycam1、简洁的界面下载安装完成后打开,主界面很简洁。2、录制屏幕选择录制,调整好要录制的区域录制完成后,可以编辑每个帧,调整帧时长,删除多

2.6k
国内开源镜像站点
精选分享

国内开源镜像站点

国内开源镜像站点开源,(Open Source)全称为开放源代码。开源就是要用户利用源代码在其基础上修改和学习的,但开源系统同样也有版权,同样也受到法律保护。国内的高校和一些企业也建立了开源镜像站点,支持开源。从开源站点上面可以下载很多开源资源(Android源码),上大学的时候就在这些镜像网站上下载Linux发行版、Qt等软件。 清华大学:http://mirrors.tuna.tsinghua

2.6k
网站被注入恶意脚本排查实录:AI 辅助安全分析实战
置顶 精选AI

网站被注入恶意脚本排查实录:AI 辅助安全分析实战

网站被注入恶意脚本排查实录:AI 辅助安全分析实战某天收到华为云监管反馈,称个人博客网站存在色情违规界面。作为技术博主,这简直是晴天霹雳——网站一直正常运行,怎么会突然出现这类问题?经过排查发现,网站被注入了恶意跳转脚本。本文记录完整的排查过程,重点分享如何借助 AI 快速定位问题并修复,希望能给遇到类似情况的开发者提供参考。1、问题发现与初步排查收到监管反馈后,第一时间打开网站检查。首页看起来完

433