android新版本(8.0以上)使用Toast的那些坑

華為、三星等機(jī)型禁用通知權(quán)限后Toast不彈出

原因

查看Toast源碼后發(fā)現(xiàn),Toast顯示要通過INotificationManager類來實(shí)現(xiàn),而當(dāng)通知禁用后,調(diào)用此類會(huì)返回異常,所以導(dǎo)致通知不顯示,源碼如下:

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) {
    // 權(quán)限禁用后走這里,這里是空方法,所以會(huì)發(fā)生既不crash又無響應(yīng)的情況
  }
}

這是一個(gè)googlebug,部分小米手機(jī)重寫了Toast代碼,所以可以正常執(zhí)行,我們可以通過反射的方式來暴力繞過,也就有了如下解決方式:

解決方法
public class ToastUtils {
    private static Object iNotificationManagerObj;

    /**
     * @param context
     * @param message
     */
    public static void show(Context context, String message) {
        show(context.getApplicationContext(), message, Toast.LENGTH_SHORT);
    }

    /**
     * @param context
     * @param message
     */
    public static void show(Context context, String message, int duration) {
        if (TextUtils.isEmpty(message)) {
            return;
        }
        //后setText 兼容小米默認(rèn)會(huì)顯示app名稱的問題
        Toast toast = Toast.makeText(context, null, duration);
        toast.setText(message);
        if (isNotificationEnabled(context)) {
            toast.show();
        } else {
            showSystemToast(toast);
        }
    }

    /**
     * 顯示系統(tǒng)Toast
     */
    private static void showSystemToast(Toast toast) {
        try {
            Method getServiceMethod = Toast.class.getDeclaredMethod("getService");
            getServiceMethod.setAccessible(true);
            //hook INotificationManager
            if (iNotificationManagerObj == null) {
                iNotificationManagerObj = getServiceMethod.invoke(null);

                Class iNotificationManagerCls = Class.forName("android.app.INotificationManager");
                Object iNotificationManagerProxy = Proxy.newProxyInstance(toast.getClass().getClassLoader(), new Class[]{iNotificationManagerCls}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //強(qiáng)制使用系統(tǒng)Toast
                        if ("enqueueToast".equals(method.getName())
                                || "enqueueToastEx".equals(method.getName())) {  //華為p20 pro上為enqueueToastEx
                            args[0] = "android";
                        }
                        return method.invoke(iNotificationManagerObj, args);
                    }
                });
                Field sServiceFiled = Toast.class.getDeclaredField("sService");
                sServiceFiled.setAccessible(true);
                sServiceFiled.set(null, iNotificationManagerProxy);
            }
            toast.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 消息通知是否開啟
     *
     * @return
     */
    private static boolean isNotificationEnabled(Context context) {
        NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context);
        boolean areNotificationsEnabled = notificationManagerCompat.areNotificationsEnabled();
        return areNotificationsEnabled;
    }
}

內(nèi)容相同Toast短時(shí)間不能重復(fù)彈出

原因

當(dāng)我們重復(fù)點(diǎn)擊Toast時(shí)候,會(huì)連續(xù)彈出很多Toast,視覺體驗(yàn)不好,于是網(wǎng)上流傳著這些解決方法:

Toast mToast;

public void showToast(String text) {
  if (mToast == null) {
    mToast = Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT);
  } else {
    mToast.setText(text);
    mToast.setDuration(Toast.LENGTH_SHORT);
  }
  mToast.show();
}

這個(gè)方法在舊版本android上沒有問題,新版本當(dāng)短時(shí)間顯示同一個(gè)Toast時(shí),會(huì)顯示不出來。

文字相同且當(dāng)前Toast正在顯示時(shí),系統(tǒng)會(huì)認(rèn)為是誤觸操作,從而屏蔽當(dāng)前顯示Toast請(qǐng)求。

出現(xiàn)這個(gè)問題據(jù)說是為了防止某些流氓app一直彈出一個(gè)模仿系統(tǒng)界面的Toast從而導(dǎo)致系統(tǒng)癱瘓。

解決方法

這是系統(tǒng)的限制,想要繞過這個(gè)限制只能自定義Toast了,這里我推薦git上的大神自定義版Toast——XToast

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容