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

相關文章
相關標籤/搜索