關於部分手機關閉通知權限導致Toast顯示不出的問題

相信很多朋友發現了華爲等部分手機把通知權限關閉之後會導致Toast無法正常彈出的情況。

原因:谷歌爲了讓應用的 Toast 能夠顯示在其他應用上面,所以使用了通知欄相關的 API,但是這個 API 隨着用戶屏蔽通知欄而變得不可用,系統錯誤地認爲你沒有通知欄權限,從而間接導致 Toast 有 show 請求時被系統所攔截。

解決方法:

 /**
     * 檢查通知欄權限有沒有開啓
     */
    public static boolean isNotificationEnabled(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            NotificationManager manager = ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE));
            if (manager != null) {
                return manager.areNotificationsEnabled();
            } else {
                return false;
            }
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
            ApplicationInfo appInfo = context.getApplicationInfo();
            String pkg = context.getApplicationContext().getPackageName();
            int uid = appInfo.uid;

            try {
                Class<?> appOpsClass = Class.forName(AppOpsManager.class.getName());
                Method checkOpNoThrowMethod = appOpsClass.getMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE, String.class);
                Field opPostNotificationValue = appOpsClass.getDeclaredField("OP_POST_NOTIFICATION");
                int value = (Integer) opPostNotificationValue.get(Integer.class);
                return (Integer) checkOpNoThrowMethod.invoke(appOps, value, uid, pkg) == 0;
            } catch (NoSuchMethodException | NoSuchFieldException | InvocationTargetException | IllegalAccessException | RuntimeException | ClassNotFoundException ignored) {
                return true;
            }
        } else {
            return true;
        }
    }

進行判斷:

    Toast toast = Toast.makeText(context, info, Toast.LENGTH_SHORT);

   //如果沒有通知權限 就強制Toast彈框
    if (!isNotificationEnabled(context)) {
        showSystemToast(toast);
        return;
    }

    //如果有通知欄權限就正常走
    ......

強制顯示方法:

 /**
     * 顯示系統Toast
     */
    private static void showSystemToast(Toast toast){
        try{
            Method getServiceMethod = Toast.class.getDeclaredMethod("getService");
            getServiceMethod.setAccessible(true);

            final Object iNotificationManager = 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
                    // 華爲p20 pro上爲enqueueToastEx
                    if("enqueueToast".equals(method.getName())
                            || "enqueueToastEx".equals(method.getName())){
                        args[0] = "android";
                    }
                    return method.invoke(iNotificationManager, args);
                }
            });
            Field sServiceFiled = Toast.class.getDeclaredField("sService");
            sServiceFiled.setAccessible(true);
            sServiceFiled.set(null, iNotificationManagerProxy);
            toast.show();

        }catch (Exception e){
            e.printStackTrace();
        }
    }
發佈了9 篇原創文章 · 獲贊 1 · 訪問量 6244
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章