欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android7.1.1出现Toast崩溃问题的解决方案

程序员文章站 2022-05-01 17:52:00
概述 toast作为android应用中最常见的一种提示方式,由于简单的api设计和简洁的交互体验被我们广泛使用,但是这并代表他很完美,本文将记录我在开发中遇到的问题。 背景 最近项目好多用户反应有...

概述

toast作为android应用中最常见的一种提示方式,由于简单的api设计和简洁的交互体验被我们广泛使用,但是这并代表他很完美,本文将记录我在开发中遇到的问题。

背景

最近项目好多用户反应有bug,然后看log出现了一个奇怪的问题,而且次数不很多,如下:

#1664 android.view.windowmanager$badtokenexception
unable to add window -- window android.view.viewrootimpl$w@4a51004 has already been added
android.view.viewrootimpl.setview(viewrootimpl.java:695)
android.view.viewrootimpl.setview(viewrootimpl.java:691)
android.view.windowmanagerglobal.addview(windowmanagerglobal.java:342)
android.view.windowmanagerimpl.addview(windowmanagerimpl.java:94)
android.widget.toast$tn.handleshow(toast.java:506)
android.widget.toast$tn$2.handlemessage(toast.java:389)
android.os.handler.dispatchmessage(handler.java:102)
android.os.looper.loop(looper.java:154) android.app.activitythread.main(activitythread.java:6292) java.lang.reflect.method.invoke(native method)
com.android.internal.os.zygoteinit$methodandargscaller.run(zygoteinit.java:906)
com.android.internal.os.zygoteinit.main(zygoteinit.java:796)

然后发现居然都是7.1.1设备才出现的,见下图:

Android7.1.1出现Toast崩溃问题的解决方案

toast显示与隐藏

首先toast显示依赖于一个窗口,这个窗口被wms管理(windowmanagerservice),当需要show的时候这个请求会放在wms请求队列中,并且会传递一个tn类型的bider对象给wms,wms并生成一个token传递给android进行显示与隐藏,但是如果ui线程的某个线程发生了阻塞,并且已经notificationmanager检测已经超时就不删除token记录,此时token已经过期,阻塞结束的时候再显示的时候就发生了异常。

在android7.1.1的toasthandleshow是这样写的:

mwm.addview(mview, mparams);

而在8.0则是这样的:

 try {
           mwm.addview(mview, mparams);
           trysendaccessibilityevent();
         } catch (windowmanager.badtokenexception e) {
           /* ignore */
         }

到这里能看到在发生异常的时候使用了try catch捕获,程序不会挂掉

解决方案


/**
 * @author ch
 * @date 2018/6/26
 * 部分7.1.1手机崩溃toast解决方案
 */
public class toastcompat {
  private static field sfield_tn;
  private static field sfield_tn_handler;
  private toast mtoast;

  static {
    try {
      sfield_tn = toast.class.getdeclaredfield("mtn");
      sfield_tn.setaccessible(true);
      sfield_tn_handler = sfield_tn.gettype().getdeclaredfield("mhandler");
      sfield_tn_handler.setaccessible(true);
     } catch (exception e) {
     }
   }

  private static void hook(toast toast) {
    try {
      object tn = sfield_tn.get(toast);
      handler prehandler = (handler) sfield_tn_handler.get(tn);
      sfield_tn_handler.set(tn, new safelyhandlerwarpper(prehandler));
     } catch (exception e) {
     }
   }

  public void showtoast(context context, charsequence cs, int length) {
    if (mtoast == null) {
      mtoast = toast.maketext(context, cs, length);
     } else {
      mtoast.settext(cs);
     }
    hook(mtoast);
    mtoast.show();
   }

  public static class safelyhandlerwarpper extends handler {
    private handler impl;

    public safelyhandlerwarpper(handler impl) {
      this.impl = impl;
     }

    @override
    public void dispatchmessage(message msg) {
      try {
        super.dispatchmessage(msg);
       } catch (exception e) {
       }
     }

    @override
    public void handlemessage(message msg) {
      impl.handlemessage(msg);//需要委托给原handler执行
     }
   }
}