您的位置:首页 > 移动开发 > Android开发

android.view.WindowManager$BadTokenException

2016-04-07 14:40 591 查看
一. 背景

输入法之前的版本在跑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方法,如:

[align=left] DialogConfirm dialog = new DialogConfirm( this);[/align]
[align=left] dialog.show();[/align]

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

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

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

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

[align=left]方法二:show时activity的判断,同样dismiss操作也是一样[/align]
[align=left]final Dialog versionDialog = new AlertDialog.Builder(context).create();[/align]
[align=left]if (context != null && context instanceof Activity[/align]
[align=left] && !((Activity) context).isFinishing()) {[/align]
[align=left] version Dialog.show();[/align]

[align=left]}[/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: