華為、三星等機型禁用通知許可權後Toast不彈出
原因
檢視Toast原始碼後發現,Toast顯示要通過INotificationManager類來實現,而當通知禁用後,呼叫此類會返回異常,所以導致通知不顯示,原始碼如下:
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) { // 許可權禁用後走這裡,這裡是空方法,所以會發生既不crash又無響應的情況 }}
這是一個google的bug,部分小米手機重寫了Toast程式碼,所以可以正常執行,我們可以通過反射的方式來暴力繞過,也就有了如下解決方式:
解決方法
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 相容小米預設會顯示app名稱的問題 Toast toast = Toast.makeText(context, null, duration); toast.setText(message); if (isNotificationEnabled(context)) { toast.show(); } else { showSystemToast(toast); } } /** * 顯示系統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 { //強制使用系統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; }}
內容相同Toast短時間不能重複彈出原因
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();}
這個方法在舊版本android上沒有問題,新版本當短時間顯示同一個Toast時,會顯示不出來。
文字相同且當前Toast正在顯示時,系統會認為是誤觸操作,從而遮蔽當前顯示Toast請求。
出現這個問題據說是為了防止某些流氓app一直彈出一個模仿系統介面的Toast從而導致系統癱瘓。
解決方法
這是系統的限制,想要繞過這個限制只能自定義Toast了,這裡我推薦git上的大神自定義版Toast——XToast
https://github.com/getActivity/XToast
文章不易,如果大家喜歡這篇文章,或者對你有幫助希望大家多多,點贊,轉發,關注 哦。文章會持續更新的。絕對乾貨!!!