今天在友盟上看到一个崩溃日志,如下:
android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@4394f040 is not valid; is your activity running? at android.view.ViewRootImpl.setView(ViewRootImpl.java:798) at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:288) at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:73) at android.app.Dialog.show(Dialog.java:287) at com.module.exam.ExamActivity$1.onTimeCountFinished(ExamActivity.java:71) at com.module.question.QuestionBaseActivity$5.onTimeCountFinished(QuestionBaseActivity.java:324) at com.utils.TimeCount.onFinish(TimeCount.java:49) at android.os.CountDownTimer$1.handleMessage(CountDownTimer.java:118) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:5493) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:525) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1209) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1025) at dalvik.system.NativeStart.main(Native Method)问题原因在于,我的ExamActivity是一个拥有CountDownTimer组件来执行倒计时业务的对象,该对象源码实现可以看到是通过消息队列机制,sendMessageDelay(msg,time)来实现的,所以就有一种情况,考试ExamActivity 在后台运行,这个时候Timer还在执行倒计时,一段时间后,考试ExamActivity被系统回收掉了,倒计时还在执行,当倒计时完成后触发 onTimerFinish()方法, 这个时候如果dialog.show() 就会报该错误。是因为这个时候window与Activity分离了,造成了窗体泄露。
解决方法:
1. 判断当前Activity是否finish()了通过 isFinishing()
2. 在show时,捕获异常
示例代码如下
setTimeCountListener(new TimeCount.OnTimeCountListener() {
@Override
public void onTimeCountFinished() {
if(!ExamActivity.this.isFinishing()){
final TipsDialog tipsDialog = new TipsDialog(ExamActivity.this);
tipsDialog.oneBtn("答题时间已到,本次考试结束,请交卷", "交卷", new View.OnClickListener() {
@Override
public void onClick(View v) {
tipsDialog.dismiss();
endExam();
}
});
try {
tipsDialog.show();
}catch (WindowManager.BadTokenException e){
LogUtil.e(e.toString());
}
}
}
这样就保证了不会发生类似错误了。
总结:
当程序中有类似 postMessageDelay 这种控制dialog显示的情况时,一定要考虑到Activity被回收这种情况。另外看Activity源码 onDestory里判断所有还在运行的dialog都会被主动销毁。这是另外一个问题。