dialog 常见bug分析not attached to window manager

dialog 常见bug分析not attached to window manager

Android小彩虹2021-07-12 22:03:1890A+A-

具体bug报错如下

java.lang.IllegalArgumentException: View=DecorView@4e9c67e[xxxx] not attached to window manager
	at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:536)
	at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:445)
	at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:124)
	at android.app.Dialog.dismissDialog(Dialog.java:410)
	at android.app.Dialog.dismiss(Dialog.java:393)
	at com.xxxx.xxxx.t9.j.close(BaseDialog.java:1)
	at com.xxxx.xxxx.xxxx.xxxx.dialog.xxxx.close(NewReceiveInvitationDialog.java:1)
	at com.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.closeNewReceiveInvitationDialog(NewReceiveInvitationDialogUtil.java:2)
	at com.youkagames.murdermystery.activity.MainActivity$c.run(MainActivity.java:4)
	at android.os.Handler.handleCallback(Handler.java:873)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at android.os.Looper.loop(Looper.java:224)
	at android.app.ActivityThread.main(ActivityThread.java:7085)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:511)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:928)

本地代码如下

        if (dialog != null) {
            dialog.close();
        }
        dialog = null;
        ```

如上报错我们一个个分析如下
就是在调用dialog的close方法内部就是调用了dialog的dismiss方法
所以导致报错的原因就是调用了dialog的dismiss方法发生了崩溃,
崩溃的方法链如下
java.lang.IllegalArgumentException: View=DecorView@4e9c67e[xxxx] not attached to window manager
	at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:536)
	at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:445)
	at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:124)
	at android.app.Dialog.dismissDialog(Dialog.java:410)
	at android.app.Dialog.dismiss(Dialog.java:393)


	所以我们先去看dialog的dismiss方法
	内部调用了dismissDialog
	WindowManagerImpl.removeViewImmediate
	WindowManagerGlobal.findViewLocked  在这里报的错的

	  final int index = mViews.indexOf(view);
        if (required && index < 0) {
            throw new IllegalArgumentException("View=" + view + " not attached to window manager");
        }
        return index;


        所以也就是index <0 导致的报错


        源码里mViews的格式如下

            @UnsupportedAppUsage
    private final ArrayList<View> mViews = new ArrayList<View>();


通过查找方法调用  只有在 void doRemoveView(ViewRootImpl root) 内调用了remove方法


所以只要知道doRemoveView这里被哪里调用了就行

本地代码会对dialog进行判空 所以是第二次调用,才会进入到close方法内,所以相对第二次的差别就是中间会调用一次dismiss  

所以我猜测的方向就是doRemoveView在dismiss内调用过 

这就是我接下来努力查找源码的方向,

所以接下来方法调用 dismissDialog ->mWindowManager.removeViewImmediate(mWindowManager 实现是WindowManagerImpl) ->WindowManagerGlobal.removeView(View view, boolean immediate);


private void removeViewLocked(int index, boolean immediate) {
    ViewRootImpl root = mRoots.get(index); //这里需要补充一下 在addview的时候 会把view同时在add到mRoots里一份  所以我们也要注重看一下root的操作  也就是 root.die()操作
    View view = root.getView();

    if (view != null) {
        InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
        if (imm != null) {
            imm.windowDismissed(mViews.get(index).getWindowToken());
        }
    }
    boolean deferred = root.die(immediate);
    if (view != null) {
        view.assignParent(null);
        if (deferred) {
            mDyingViews.add(view);
        }
    }
}

最后在ViewRootImpl 内通过handler发送MSG_DIE  的doDie()方法内看到调用了 WindowManagerGlobal.getInstance().doRemoveView(this); 也就是我们日思夜想的那个方法 


所以最终是印证了  在dismiss内对mView进行了清除  

至于如何避免这个bug呢

可以在dismissDialog方法内找到答案
代码如下

void dismissDialog() { if (mDecor == null || !mShowing) { return; }

    if (mWindow.isDestroyed()) {
        Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
        return;
    }

    try {
        mWindowManager.removeViewImmediate(mDecor);
    } finally {
        if (mActionMode != null) {
            mActionMode.finish();
        }
        mDecor = null;
        mWindow.closeAllPanels();
        onStop();
        mShowing = false;

        sendDismissMessage();
    }
}

也就是这里 

mDecor = null; mWindow.closeAllPanels(); onStop(); mShowing = false;


dialog给我们提供的方法就是

public boolean isShowing() {
    return mDecor == null ? false : mDecor.getVisibility() == View.VISIBLE;
}

所以针对我这个bug的解决办法只需在不为null的同时判断一下 Dialog.isShowing() 即可
    if (dialog != null && dialog.isShowing()) {
        dialog.close();
    }

以上就是通过源码分析一个bug的过程,可能baidu 或者Google更快,但是缺少了过程,在不忙的时候 可以通过源码的报错打印来分析整个流程,从而找到解决办法

最后总结就是  不要在dialog没有创建 或者是 已经创建过了但是dismiss后在调用dismiss  


点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1

联系我们