关于dialog,PopupWindow,SoftInputBoard的弹出时机的问题

Android中Window有三大层次,
dialog,PopupWindow,SoftInputBoard这些都属于第三层次-不能独立存在,必须依附于其他window,
体现在代码上就是必须能够拿到activity的windowtoken,才能够成功弹出

也就是activity在windowmanager的管理期内,对应的回调api是onAttachedToWindow和onDetachedFromWindow,在这个期间,我们可以认为,activity的windowtoken不为null.
BadTokenException有两种情况,一种是比activity先弹,一种是activity结束了才弹

activity几个生命周期回调的间隔时间

测试方法内均没有耗时的计算,下面的日志显示,从oncreate开始算起,20ms后onstart,75ms后onResume,312ms后onAttachedToWindow. 实际数据大概就是这个数量级.
dialog调用show方法到真正显示在屏幕上,也是两三百毫秒这个数量级,和activity的显示一样,都涉及到windowmanager的binder远程调用.

activity生命周期回调时间.png
activity生命周期图

测试

https://github.com/hss01248/DialogUtil

crash oncreate
crash ondestory

to eliminate the crash:

Never declare/instantiate Dialogs as local variables.
Make all Dialogs instance variables of the Activity.
Override onDestroy and call if(dialog != null) dialog.dismiss();

在baseactivity中:

@Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        if(isFirstIn){
            onFirstAttach();
            isFirstIn = false;
        }else {
            onNotFirstAttach();
        }
    }

    /**
     * 非第一次onResume
     */
    protected void onNotFirstAttach() {}

    /**
     * 第一次onResume
     */
    protected  void onFirstAttach(){}

在 onFirstAttach()中弹出dialog和popupwindow,弹出时注意加上判定和try-catch:

@Override
    protected void onFirstAttach() {
        super.onFirstAttach();
    ...

                   if(SplashActivity.this.isFinishing() ){
                        return;
                    }
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                        if(SplashActivity.this.isDestroyed()){
                            return;
                        }
                    }

                    try {
                        languageSelectDialog.show();
                    }catch (Exception e){//show需要200-300ms,如果期间activity被finish了,也会抛BadTokenException
                        e.printStackTrace();
                    }

        
    }

在onDestory中结束dialog

@Override
    protected void onDestroy() {
        super.onDestroy();
        if(languageSelectDialog !=null && languageSelectDialog.isShowing()){
            languageSelectDialog.dismiss();
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容