Toast使用了INotificationManager类,和通知栏有关,如果关闭了通知权限,则Toast不能弹出,如三星、华为等手机在关闭通知栏的情况下就不能使用吐司了。当然国内厂商很多,总会有厂商来自己处理这个东西,比如小米,小米在关闭通知的情况下还可以弹出吐司。
不能强迫用户打开通知,一些很重要的情况下必须要告知用户一些信息,如付款失败、订单创建失败,这种时候我们就让机把吐司吐出来。实现原理就是冒充系统消息,使用系统吐司。
实现思路是APP调用NotificationManager弹出吐司,就需要给Manager传一个包名,manager获取到该APP包名后先判断该APP是否有通知权限,如果没有则终止,不弹出吐司,有则弹出。我们想在APP没有通知权限下也能弹出吐司就要给manager传“android”,这是使用系统Toast意思,毕竟系统的不会被禁。
1.首先判断当前通知权限是否可用
private static boolean isNotificationEnabled(Context context) {
NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context);
boolean areNotificationsEnabled = notificationManagerCompat.areNotificationsEnabled();
return areNotificationsEnabled;
}
2.如果当前通知权限没被禁,那就直接show就行,如果被禁就使用系统吐司
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 Objectinvoke(Object proxy, Method method, Object[] args)throws Throwable {
//强制使用系统Toast
if ("enqueueToast".equals(method.getName())
||"enqueueToastEx".equals(method.getName())//华为p20 pro上为enqueueToastEx
||"enqueueToastLog".equals(method.getName())) {//三星s10 enqueueToastLog
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();
}
}
method.getName() 的判断需要注意,这里是根据手机来区分的,如果使用的手机获取到的方法名不在这里面就需要自己添加(测试比较麻烦,需要对单独手机进行测试,比如我用的是三星s10,s9可能就不是这个值就需要新增)
网友评论