Android疑难杂症之——Toast引发的BadTokenException

1.现象分析

当咱们将targetSDK升级到26以上后,发现项目中报告了不少BadTokenException异常,查看堆栈几乎都与Toast有关:java

crash堆栈

经过堆栈查看源码知道Toast是经过内部类TN的handleShow()方法来展现浮窗,而这个方式是可能会抛出WindowManager.BadTokenException异常的,虽然api26以后google对这个异常进行了捕获,使其不至于形成应用crash,但在26以前并无作任何处理:segmentfault

在api26以前(特别是26)的机器上有一个稳定复现的路径,在主线程调用Toast的show方法后,阻塞3s左右就会抛出上面的BadTokenException异常并致使crash:api

QQToast.makeText(this, "哈哈哈", Toast.LENGTH_SHORT).show();
try {
    Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
复制代码

这是能够获得crash堆栈: ide

复现的crash堆栈

2.解决方案

那么在api26以前,咱们能够模仿Android8中google针对这个异常的处理方式,经过反射自定义一个Handler的代理,使其捕获这个异常,从而保证应用不会所以而crashui

private static class HandlerProxy extends Handler {
    private Handler mHandler;

    public HandlerProxy(Handler handler) {
        this.mHandler = handler;
    }

    @Override
    public void handleMessage(Message msg) {
        try {
            mHandler.handleMessage(msg);
        } catch (Throwable throwable) {
            GLog.e(TAG, "toast error: " + throwable.getMessage());
        }
    }
}
复制代码

首先定义个Handler的代理,主要用来对Toast中TN的Handler作一个封装this

下面经过反射的方式对Toast中TN的Handler作处理:google

if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
    try {
        /** * 获取mTN对象 * 并获取它的class类型 */
        Class<Toast> clazzToast = Toast.class;
        Field fieldTN = clazzToast.getDeclaredField("mTN");
        fieldTN.setAccessible(true);
        Object objTn = fieldTN.get(toast);
        Class clazzTn = objTn.getClass();
        /** * 获取TN中的mHandler对象 * 而后用咱们自定义的HandlerProxy类包裹它 * 使得它能捕获异常 */
        Field fieldHandler = clazzTn.getDeclaredField("mHandler");
        fieldHandler.setAccessible(true);
        fieldHandler.set(objTn, new HandlerProxy((Handler) fieldHandler.get(objTn)));
    } catch (Throwable throwable) {
        GLog.e(TAG, "hack toast handler error: " + throwable.getMessage());
    }
}
复制代码

代码注释写的比较明白,也不难。其实这里就主要是模仿8.0的处理方式来捕获了这个BadTokenExceptionspa

参考线程

Android 7.X Toast Bug代理

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

Toast的BadTokenException

相关文章
相关标签/搜索