天天看點

Toast彈不出來之謎

前言

今天早上測試應用的時候,忽然發現Toast彈不出來了,我用的華為測試機,以為是通知權限被關了,後來發現是開着的,這就納了悶了,這個Toast工具類用了好長時間了,後來發現這Toast原來還能這樣...

正文

以前工具類是這個樣子的(華為的通知權限放在了其他的地方)

final public class ToastUtil {
    private static Toast toast;//單例的toast

    /**
     * 顯示Toast
     */
    public static void showToast(int text) {
        showToast(App.getInstance().getResources().getString(text));
    }

    public static void showToast(String text) {
        if (toast == null)
            toast = Toast.makeText(App.getInstance(), text, Toast.LENGTH_SHORT);
        toast.setText(text);
        toast.show();
    }
}
           

平時用的都是好好的,為什麼不能用了呢?

然後我通過debug發現所有路徑都走了,說明是調用成功了,後來想起來,是不是子線程的問題,列印了一下線程資訊:

Thread.currentThread().getName();
           

發現果然第一次調用沒有跑在主線程中,這就尴尬了,在子線程中建立,并show()竟然沒有報錯,而且子線程建立的在主線程調用show()也沒有報錯,隻是不顯示

到show()的源碼中發現,被try了..

/**
     * Show the view for the specified duration.
     */
    public void show() {
        if (mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }

        INotificationManager service = getService();
        String pkg = mContext.getOpPackageName();
        TN tn = mTN;
        tn.mNextView = mNextView;

        try {
            service.enqueueToast(pkg, tn, mDuration);
        } catch (RemoteException e) {
            // Empty
        }
    }
           

修改方法

在建立和show的時候可以判斷是否是主線程,如果不是就放到主線程中就ok了

修改後的代碼,嗯,這樣就可以了(雖然有強迫症看到這代碼很不爽,但是首先得能用)

final public class ToastUtil {
    private static Toast toast;//單例的toast

    /**
     * 顯示Toast
     */
    public static void showToast(int text) {
        showToast(App.getInstance().getResources().getString(text));
    }

    public static void showToast(final String text) {
        if (toast == null) {
                //AppManager.getAppManager().currentActivity()是使用工具類擷取目前Activity對象的方法
                AppManager.getAppManager().currentActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        toast = Toast.makeText(App.getInstance(), text, Toast.LENGTH_SHORT);
                        toast.show();
                    }
                });
        } else {
            //如果show()是在子線程觸發的,則在主線程來顯示
            if ("main".equals(Thread.currentThread().getName())) {
                toast.setText(text);//将文本設定給toast
                toast.show();
            } else {
                AppManager.getAppManager().currentActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        toast.setText(text);//将文本設定給toast
                        toast.show();
                    }
                });
            }
        }
    }
}
           

ps:近些天看到了一個類似的情況,比我分析的源碼更多:https://mp.weixin.qq.com/s/xzeihP6nexNyBLjMuxraJg

end

繼續閱讀