android.view.WindowManager$BadTokenException

来源:互联网 发布:windows微信机器人 编辑:程序博客网 时间:2024/05/22 12:31
一. 背景
输入法之前的版本在跑Monkey测试以及崩溃后台,会出现比较多的android.view.WindowManager$BadTokenException的崩溃,崩溃堆栈如下:
StackTrace=Unable to add window -- token android.os.BinderProxy@447ebf50 is not valid; is your activity running?android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@447ebf50 is not valid; is your activity running? 
at android.view.ViewRootImpl.setView(ViewRootImpl.java:806) 
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:278) 
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69) 
at android.app.Dialog.show(Dialog.java:304) 
从堆栈中我们可以看到是在使用dialog中出现的问题,因此我们检查了输入法所有使用dialog的地方,整理出dialog的2种显示方式。

二. Dialog的两种显示方式
方法1:
 最常用的使用方法:activity中直接进行dialog的构造,并调用dialog.show方法,如:
   DialogConfirm dialog = new DialogConfirm( this);
   dialog.show();

方法2:
 使用token进行dialog的显示。无activity的时,如何显示dialog,比如输入法键盘区是没有activity,这个时候就需要使用token来完成dialog的显示,方法:
   mDownloadLanguageDialog = (DialogConfirm) PreferenceDialogFactory.produceDialog(
                     getContext(), DialogTypeId.TYPE_NORMAL_MESSAGE );
   IBinder binder = getWindowToken();
   if ( binder == null || !binder .isBinderAlive()) {
       return;
   }
   Window window = mDownloadLanguageDialog.getWindow();
   WindowManager.LayoutParams lp = window.getAttributes();
   lp. token = binder;
   lp. type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG ;
   window.setAttributes(lp);
   window.addFlags(WindowManager.LayoutParams. FLAG_ALT_FOCUSABLE_IM );
   mDownloadLanguageDialog .show();

两种dialog的显示方式的原理都是如方法2,只是第一种方法由dialog内部进行了方法2的逻辑处理。

三. 为何出现BadTokenException
 从显示方式中我们知道dialog的隐藏和显示是必现依赖于窗口的token才能进行操作的,因此当dialog需要依附的窗口token无效的时候,
 就会出现BadTokenException的问题。而token什么时候会无效呢,也就是当token被回收时, 比如activity中进行dialog隐藏或者展示时,
 activity被销毁,token也会被回收; 比如输入法键盘区的View被重新构造时,token也会被销毁。

四. 如何避免
由于dialog的显示也是通过handler进行处理的,因此当dialog的show及dismiss时,需要进行token的判断,方法: 
 方法一: show时binder的判断,同样dismiss操作也是一样
final DialogConfirm mNormalDialog = (DialogConfirm) PreferenceDialogFactory
                     . produceDialog(context, DialogTypeId.TYPE_NORMAL_MESSAGE );
           Window window = mNormalDialog.getWindow();
           WindowManager.LayoutParams lp = window.getAttributes();
           lp. token = ib;
           lp. type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG ;
           window.setAttributes(lp);
          window.addFlags(WindowManager.LayoutParams. FLAG_ALT_FOCUSABLE_IM );
            // 如果dialog的token不为空且对象还存在,才进行dialog的显示
            if (ib != null && ib.isBinderAlive()) {
                mNormal Dialog.show();
           } else {
                 return null;
           }

方法二:show时activity的判断,同样dismiss操作也是一样
final Dialog versionDialog = new AlertDialog.Builder(context).create();
if (context != null && context instanceof Activity
                     && !((Activity) context).isFinishing()) {
                version Dialog.show();
}
0 0
原创粉丝点击