View not attached to window manager异常

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

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

相关推荐

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

国内开源镜像站点

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

2.4k
置顶 精选
博客七周年:AI 一天完成整体重构
AI

博客七周年:AI 一天完成整体重构

博客从 2019 年国庆用 Xiuno BBS 搭建,到 2026 年国庆整整七年。868 篇文章、53 条评论、6060 个代码块,这次与 AI Agent 结对,一天完成从 PHP 论坛到 Next.js 的整体重构与无损迁移。

23
R8编译问题:Missing classes detected while running R8
分享

R8编译问题:Missing classes detected while running R8

R8编译问题:Missing classes detected while running R8Android R8是一个代码混淆和压缩工具,可以将应用程序的大小和安全性优化。它引入了一些新功能,如成员内省、混淆指针、类内省等。但R8使用起来一直不友好,因为自从使用R8之后编译问题不断。主要还是和混淆相关,经常报错,最近又遇到一个:Missing classes detected while ru

8.4k