華為、三星等機型禁用通知權限后Toast不彈出
原因
查看Toast
源碼后發(fā)現,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) {
// 權限禁用后走這里廊酣,這里是空方法食侮,所以會發(fā)生既不crash又無響應的情況
}
}
這是一個google
的bug
荷并,部分小米手機重寫了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 兼容小米默認會顯示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 {
//強制使用系統(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;
}
}
內容相同Toast短時間不能重復彈出
原因
當我們重復點擊Toast
時候翩伪,會連續(xù)彈出很多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
正在顯示時囊颅,系統(tǒng)會認為是誤觸操作,從而屏蔽當前顯示Toast
請求傅瞻。
出現這個問題據說是為了防止某些流氓app
一直彈出一個模仿系統(tǒng)界面的Toast
從而導致系統(tǒng)癱瘓踢代。
解決方法
這是系統(tǒng)的限制,想要繞過這個限制只能自定義Toast
了嗅骄,這里我推薦git上的大神自定義版Toast
——XToast