android.view.WindowManager$BadTokenException

本文分析了在Android应用中出现BadTokenException的原因,特别是在使用Dialog时。文章详细介绍了两种显示Dialog的方法,并给出了避免该异常的有效策略。
一. 背景
输入法之前的版本在跑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();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值