• Android7.1.1系统,Toast的Exception: android.view.WindowManager$BadTokenException解决


     7.1.1系统版本,我发现问题很多,比如 有个接口字段用了base64,但是我添加包的时候用的 java.util.base64 结果会崩溃报错,网上搜索后要用 android.util.base64 来解决,

     今天发现突然有崩溃,看了下崩溃信息

    1. Fatal Exception: android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@fcd9ef6 is not valid; is your activity running?
    2.        at android.view.ViewRootImpl.setView(ViewRootImpl.java:806)
    3.        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:369)
    4.        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:94)
    5.        at android.widget.Toast$TN.handleShow(Toast.java:459)
    6.        at android.widget.Toast$TN$2.handleMessage(Toast.java:342)
    7.        at android.os.Handler.dispatchMessage(Handler.java:102)
    8.        at android.os.Looper.loop(Looper.java:186)
    9.        at android.app.ActivityThread.main(ActivityThread.java:6491)
    10.        at java.lang.reflect.Method.invoke(Method.java)
    11.        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:914)
    12.        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:804)

    一下解决办法是我网上转过来的,经过自测后,解决了我的问题,我这边记录

    这个BadTokenException是由谷歌系统产生的BUG,只有在系统7.x会产生,谷歌团队已经在8.0以上系统修复,但对于开发者来说面对android的7.x系统的用户不得不去解决这个BUG

    BadTokenException产生的原因及谷歌的解决方案
    在Toast.show()之后,UI线程做了耗时的操作阻塞了Handlermessage的处理。7.X系统对TYPE_TOAST的Window类型做了超时限制,绑定了WindowToken,如果UI在这段时间内没有执行完,Toast.show()内部的handlermessage得不到执行,NotificationManageService那端会把这个Toast取消掉,同时把Toast对于的windowtoken置为无效。等App端真正需要显示Toast时,因为windowtoken已经失效,ViewRootImpl就抛出了上面的异常。

    我们看一下谷歌源码是如何解决的,8.0以上系统Toast的handleShow 方法里面的一段代码,mWM.addView(mView, mParams),是被try catch 了,在7.x的系统是没有被catch住的(这里就不贴代码了)
     

    看到谷歌的解决方案,所以我们7.x的解决方案其实也是把异常给catch 住。那如何catch住呢?我们看Toast的源码时里面有个mHandler的成员变量,我们想办法把分发消息的dispatchMessage(Message msg) 异常捕获不就可以了吗?这个其实和谷歌的解决方案一样。

     这里我们采用Java的反射机制
     

    1. try {
    2. sField_TN = Toast.class.getDeclaredField("mTN");
    3. sField_TN.setAccessible(true);
    4. sField_TN_Handler = sField_TN.getType().getDeclaredField("mHandler");
    5. sField_TN_Handler.setAccessible(true);
    6. Object tn = sField_TN.get(toast);
    7. //获取原有的Handler
    8. Handler preHandler = (Handler) sField_TN_Handler.get(tn);
    9. //设置自定义的Handler
    10. sField_TN_Handler.set(tn, new SafelyHandlerWarpper(preHandler));
    11. } catch (Exception e) {
    12. }

      这里我们设置自定义的Handler

    1. /****
    2. * 自定义Handler catch处理异常
    3. */
    4. public static class SafelyHandlerWarpper extends Handler {
    5. private Handler impl;
    6. public SafelyHandlerWarpper(Handler impl) {
    7. this.impl = impl;
    8. }
    9. @Override
    10. public void dispatchMessage(Message msg) {
    11. try {
    12. super.dispatchMessage(msg);
    13. } catch (Exception e) {
    14. }
    15. }
    16. @Override
    17. public void handleMessage(Message msg) {
    18. impl.handleMessage(msg);//需要委托给原Handler执行
    19. }
    20. }
     

    我们把dispatchMessage给catch住,handleMessage处理的消息具体实现还是原有的handler处理逻辑,不去影响原有的处理逻辑,我们只是catch住异常防止应用崩溃。

    完整实现类如下:

    1. /***
    2. *
    3. * 解决部分7.1.1手机崩溃Toast解决方案
    4. */
    5. public class ZtAppCompatToast {
    6. private static Field sField_TN;
    7. private static Field sField_TN_Handler;
    8. private Toast mToast;
    9. static {
    10. //安卓7.0的做处理,其它版本系统的不用处理
    11. if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
    12. try {
    13. sField_TN = Toast.class.getDeclaredField("mTN");
    14. sField_TN.setAccessible(true);
    15. sField_TN_Handler = sField_TN.getType().getDeclaredField("mHandler");
    16. sField_TN_Handler.setAccessible(true);
    17. } catch (Exception e) {
    18. }
    19. }
    20. }
    21. public ZtAppCompatToast(Context context) {
    22. mToast = new Toast(context);
    23. setHook(mToast);
    24. }
    25. public Toast getToast() {
    26. return mToast;
    27. }
    28. public static Toast makeText(Context context,
    29. CharSequence text, int duration) {
    30. Toast toast = Toast.makeText(context, text, duration);
    31. setHook(toast);
    32. return toast;
    33. }
    34. public static Toast makeText(Context context,
    35. int textSrc, int duration) {
    36. Toast toast = Toast.makeText(context, textSrc, duration);
    37. setHook(toast);
    38. return toast;
    39. }
    40. private static void setHook(Toast toast) {
    41. //安卓7.0的做处理,其它版本系统的不用处理
    42. if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
    43. hook(toast);
    44. }
    45. }
    46. /*****
    47. *
    48. * @param toast
    49. */
    50. private static void hook(Toast toast) {
    51. try {
    52. Object tn = sField_TN.get(toast);
    53. Handler preHandler = (Handler) sField_TN_Handler.get(tn);
    54. sField_TN_Handler.set(tn, new SafelyHandlerWarpper(preHandler));
    55. } catch (Exception e) {
    56. }
    57. }
    58. /****
    59. * 自定义Handler catch处理异常
    60. */
    61. public static class SafelyHandlerWarpper extends Handler {
    62. private Handler impl;
    63. public SafelyHandlerWarpper(Handler impl) {
    64. this.impl = impl;
    65. }
    66. @Override
    67. public void dispatchMessage(Message msg) {
    68. try {
    69. super.dispatchMessage(msg);
    70. } catch (Exception e) {
    71. }
    72. }
    73. @Override
    74. public void handleMessage(Message msg) {
    75. impl.handleMessage(msg);//需要委托给原Handler执行
    76. }
    77. }
    78. }

  • 相关阅读:
    PDF标准详解(三)—— PDF坐标系统和坐标变换
    自回归策略是什么
    JVM总结
    PHP+Nginx配置备忘
    搜索引擎项目开发过程以及重难点整理
    【分布式云储存】Springboot微服务接入MinIO实现文件服务
    MySQL事务(清晰易懂)
    Day683.AprEndpoint组件:Tomcat APR提高I/O性能的秘密 -深入拆解 Tomcat & Jetty
    【深度学习笔记】9_5 多尺度目标检测
    对换对排列奇偶性的影响
  • 原文地址:https://blog.csdn.net/yangbin0513/article/details/127044042