相信很多朋友发现了华为等部分手机把通知权限关闭之后会导致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();
}
}