关于Android7.x系统Toast显示异常BadTokenException解决方案

匿名 (未验证) 提交于 2019-12-03 00:30:01
Fatal Exception: android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@fcd9ef6 is not valid; is your activity running?        at android.view.ViewRootImpl.setView(ViewRootImpl.java:806)        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:369)        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:94)        at android.widget.Toast$TN.handleShow(Toast.java:459)        at android.widget.Toast$TN$2.handleMessage(Toast.java:342)        at android.os.Handler.dispatchMessage(Handler.java:102)        at android.os.Looper.loop(Looper.java:186)        at android.app.ActivityThread.main(ActivityThread.java:6491)        at java.lang.reflect.Method.invoke(Method.java)        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:914)        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:804) 

这是线上一直存在的一个异常,由于无法复现,所以没有管他。最近几天崩溃数暴增,忍无可忍,通过参考美团的这篇文章把这个异常解决了一下。

调用Toast的show()方法后,将主线程阻塞5s,即可稳定复现这个异常

Toast.makeToast(context, "123", LENGTH_LONG).show();  Thread.sleep(5*1000); 

出现这个异常的原因就是Toast显示时会有一个Token,这个Token是由WindowManager创建并管理的,重点是这个Token有时间限制,当超过了一个Toast的显示时长(LENGTH_LONG)后,会把这个Token置为无效。所以把Toast延迟显示后,使用的Token就已经过期了。

这个问题全部发生在7.x系统上,在8.x的系统已经把显示Toast视图的位置添加了try catch捕获。如下代码所示:

public void handleShow(IBinder windowToken) {     ...     try {          mWM.addView(mView, mParams);          trySendAccessibilityEvent();      } catch (WindowManager.BadTokenException e) {          /* ignore */      } } 

在Android7.x系统上,利用反射机制,改由我们自己Handler处理Toast的显示,并添加BadTokenException异常捕获。

     int sdkInt = Build.VERSION.SDK_INT;     if (sdkInt >= Build.VERSION_CODES.N && sdkInt < Build.VERSION_CODES.O && !isReflectedHandler) {         reflectTNHandler(mToast);         //这里为了避免多次反射,使用一个标识来限制         isReflectedHandler = true;     } 
 private static void reflectTNHandler(Toast toast) {         try {             Field tNField = toast.getClass().getDeclaredField("mTN");             if (tNField == null) {                 return;             }             tNField.setAccessible(true);             Object TN = tNField.get(toast);             if (TN == null) {                 return;             }             Field handlerField = TN.getClass().getDeclaredField("mHandler");             if (handlerField == null) {                 return;             }             Timber.d("TN class is %s.", TN.getClass());             handlerField.setAccessible(true);             handlerField.set(TN, new ProxyTNHandler(TN));         } catch (NoSuchFieldException e) {             e.printStackTrace();         } catch (IllegalAccessException e) {             e.printStackTrace();         }     } 
  1. 反射拿到Toast的成员变量TN mTN
  2. 反射拿到mTN的成员变量Handler mHandler
  3. 替换为我们自己的Handler实现
  4. 在show Toast时添加异常捕获,并调用mTN响应的Toast显示方法handleShow
 //Toast$TN持有的Handler变量     private static class ProxyTNHandler extends Handler {         private Object tnObject;         private Method handleShowMethod;         private Method handleHideMethod;          ProxyTNHandler(Object tnObject) {             this.tnObject = tnObject;             try {                 this.handleShowMethod = tnObject.getClass().getDeclaredMethod("handleShow", IBinder.class);                 this.handleShowMethod.setAccessible(true);                 Timber.d("handleShow method is %s", handleShowMethod);                 this.handleHideMethod = tnObject.getClass().getDeclaredMethod("handleHide");                 this.handleHideMethod.setAccessible(true);                 Timber.d("handleHide method is %s", handleHideMethod);             } catch (NoSuchMethodException e) {                 e.printStackTrace();             }         }          @Override         public void handleMessage(Message msg) {             switch (msg.what) {                 case 0: {                     //SHOW                     IBinder token = (IBinder) msg.obj;                     Timber.d("handleMessage(): token is %s", token);                     if (handleShowMethod != null) {                         try {                             handleShowMethod.invoke(tnObject, token);                         } catch (IllegalAccessException e) {                             e.printStackTrace();                         } catch (InvocationTargetException e) {                             e.printStackTrace();                         } catch (WindowManager.BadTokenException e) {                             //显示Toast时添加BadTokenException异常捕获                             e.printStackTrace();                             Timber.e(e, "show toast error.");                         }                     }                     break;                 }                  case 1: {                     //HIDE                     Timber.d("handleMessage(): hide");                     if (handleHideMethod != null) {                         try {                             handleHideMethod.invoke(tnObject);                         } catch (IllegalAccessException e) {                             e.printStackTrace();                         } catch (InvocationTargetException e) {                             e.printStackTrace();                         }                     }                     break;                 }                 case 2: {                     //CANCEL                     Timber.d("handleMessage(): cancel");                     if (handleHideMethod != null) {                         try {                             handleHideMethod.invoke(tnObject);                         } catch (IllegalAccessException e) {                             e.printStackTrace();                         } catch (InvocationTargetException e) {                             e.printStackTrace();                         }                     }                     break;                 }              }             super.handleMessage(msg);         }     }  
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!