以下代码以compileVersion=28
作为示例来演示
-
添加NotificationChannel(必需)
当
compileVersion>=26
且Notification没有设置channelId时,8.0的系统上通知不会弹出,在logcat的error级别显示NotificationService提示日志:No Channel found for pkg=aaa, channelId=null,...notification=Notification(channel=null ...)
可以使用两种方式给Notification对象添加channelId:
NotificationCompat.Builder(context, channelId)...build()
或者build.setChannelId(channelId)...
-
NotificationChannel
创建: NotificationChannel(String channelId, CharSequence name, @Importance int importance)-
channelId
build时设置给Notification的id -
name
显示在通知管理里的标题 -
importance
此通道的重要性,5个等级范围
-
在通知被
notify(notification)
之前必须确保通知的NotificationChannel已经被注册,api: createNotificationChannel(channel)
-
机型适配
-
有的手机在添加channel后仍然无法弹出通知。追踪logcat发现有这么一句:
E/NotificationService: Suppressing notification from package by user request.
用户请求抑制此通知?追踪notify源码找到
NotificationManagerService
的enqueueNotificationInternal(......)
方法:void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,final int callingPid, final String tag, final int id, final Notification notification,int incomingUserId) { ... if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r)) {return;} ... mHandler.post(new EnqueueNotificationRunnable(userId, r)); } private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,NotificationRecord r) { ...// blocked apps if (isBlocked(r, mUsageStats)) {return false;} return true; } protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) { final String pkg = r.sbn.getPackageName(); final int callingUid = r.sbn.getUid(); final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid); if (isPackageSuspended) { Slog.e(TAG, "Suppressing notification from package due to package suspended by administrator."); usageStats.registerSuspendedByAdmin(r); return isPackageSuspended; } final boolean isBlocked = mRankingHelper.getImportance(pkg, callingUid) == NotificationManager.IMPORTANCE_NONE || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE; if (isBlocked) { Slog.e(TAG, "Suppressing notification from package by user request."); usageStats.registerBlocked(r); } return isBlocked; }
最终的
isBlocked
判断条件满足,导致notify操作被中断return。 -
目前为止国产rom现状是:
- 通知总权限在华为EMUI/魅族flyme/原生系统上默认是打开的,MIUI/VIVO/OPPO是默认关闭的
-
渠道开关在OPPO手机上是默认关闭的,在开启总权限后还需要开启相关的类别(对应channel的name)才能正常使用。而测试的其他手机在开启总开关后自动开启channelId的通知开关。
-
这么检测通知权限:
public static boolean isNotificationEnabled(Context context,String channelId) { NotificationManagerCompat managerCompat = NotificationManagerCompat.from(context); NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); boolean returnValue = managerCompat.areNotificationsEnabled(); if(manager == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.O){ return returnValue; } NotificationChannel channel = manager.getNotificationChannel(channelId); if(channel == null){ channel = new NotificationChannel(channelId,"我的推送类别",NotificationManager.IMPORTANCE_HIGH); manager.createNotificationChannel(channel); 下面的获取操作必需,创建的channel和获取到的channel的IMPORTANCE可能不一样,OPPO默认IMPORTANCE_NONE。 channel = manager.getNotificationChannel(channelId); } return returnValue && channel.getImportance() != NotificationManager.IMPORTANCE_NONE; }
-
跳转通知管理页面的代码:
boolean isNotifyEnable = NotificationManagerCompat.from(context).areNotificationsEnabled(); boolean isChannelEnable = true; if (Build.VERSION.SDK_INT >= 26) { isChannelEnable = channel.getImportance() != NotificationManager.IMPORTANCE_NONE; } if (isNotifyEnable && isChannelEnable) { manager.notify(notifyId, notification); 正常notify } else if (!isNotifyEnable) { Intent intent = new Intent(); if(!(context instanceOf Activity)){ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } if (Build.VERSION.SDK_INT >= 26) { intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS); context.startActivity(intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName())); }else if (Build.VERSION.SDK_INT >= 21) { intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); context.startActivity(intent.putExtra("android.provider.extra.APP_PACKAGE", getPackageName())); } else if (Build.VERSION.SDK_INT >= 9) { intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setData(Uri.fromParts("package", getPackageName(), null)); context.startActivity(intent); } else {低于9没有适配必要} }else{ 只打开了通知开关,但是关闭了当前channel的通知,开发者需要根据通知重要性,自行决定如何提示用户 }
- 查看api21与api26的源码发现
Settings. ACTION_APP_NOTIFICATION_SETTINGS
的值其实就是"android.settings. APP_NOTIFICATION_SETTINGS"
,只是在26前这个常量是隐藏的。因此上述代码可简化掉api26那部分。 -
创建的NotificationChannel和notify时获取的NotificationChannel可能是不同的。因为在service层保存的实现方法国产rom做了更改。以防万一,请使用
manager.getNotificationChannel(channelId)
生成的NotificationChannel
对象 - 测试时发现vivo X21有两个通知管理页面:右边明显很漂酿啊有木有
dumpsys activity | grep -i run
找到落地页(包名:com.android.systemui Activity名:com.vivo.systemui.statusbar.notification.settings.channels.NotificationSettingsActivity) 跑了下崩了,提示Permission Denial: starting Intent{...}not exported from uid 10023
。activity没有设置为exported,有空了看看源码是否有破解方法...
- 查看api21与api26的源码发现
-
-
CompileSdkVersion<26时的机型适配
- NotificationChannel这个API是在安卓8.0引入,所以当编译版本低于26时,不能加入channel,但是经过测试在vivo的安卓8.0手机上提示
Suppressing notification from package by user request
通知无法弹出 - areNotificationsEnabled 在安卓7.0(api24)加入,对应的support支持包最低v7:24.0.0。如果编译版本不低于api24,做如下适配:
boolean isNotifyEnable = NotificationManagerCompat.from(this).areNotificationsEnabled(); if(isNotifyEnable){ manager.notify(notifyId,notification); }else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { startActivity(new Intent("android.settings.APP_NOTIFICATION_SETTINGS") .putExtra("android.provider.extra.APP_PACKAGE", getPackageName())); } else/* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) */{ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setData(Uri.fromParts("package", getPackageName(), null)); startActivity(intent); } 考虑到目前app没人再适配android 2.x,因此最后一个else省略
- 如果编译版本低于24,可参考高版本api自行实现,这里就不贴出了。目前各应用商店都在强制提升targetVersion,以后如果是上应用市场的app不会再有低于26的编译版本了
- NotificationChannel这个API是在安卓8.0引入,所以当编译版本低于26时,不能加入channel,但是经过测试在vivo的安卓8.0手机上提示