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远程调用.
测试
https://github.com/hss01248/DialogUtil
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();
}
}