-
1:添加NotificationChannel
-
1.1:Notification如果没有设置channelId,通知不会弹出,在logcat的error级别显示NotificationService提示日志:
No Channel found for pkg=aaa, channelId=null,...notification=Notification(channel=null ...)
-
1.2:可见在安卓8.0及以上通知是必须配置ChannelId的。有两种方式给Notification对象添加channelId:
- 1.2.1:
Notification notify = new NotificationCompat.Builder(this, channelId)...
- 1.2.2:
NotificationCompat.Builder build.setChannelId(channelId)...
- 1.2.1:
-
1.3:
NotificationChannel
的创建与注册public NotificationChannel(String id, CharSequence name, @Importance int importance)
-
id
即channelId
-
name
显示在通知管理里的标题 -
importance
代表此通道的重要性,从高到低限定了5个等级范围,有不同的表现行为
-
在
NotificationManager#notify()
前需要确保channelId
已经被创建过:NotificationManager#createNotificationChannel(NotificationChannel);
-
-
2:targetSdkVersion>=26时的机型适配
-
2.1: 在实际开发中发现:即使按照官方规范添加了
NotificationChannel
,好多手机上依然无法弹出通知。追踪logcat发现有这么一句:E/NotificationService: Suppressing notification from package by user request.
用户请求抑制此通知?追查源码
NotificationManager#notify(int id, Notification notification)
最终调用了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。目前为止:通知权限在华为EMUI/魅族flyme/原生系统上默认是打开的,MIUI/VIVO/OPPO是默认关闭的,好吧为了通知能够正常使用先上一段权限工具代码试试:boolean enabled = NotificationManagerCompat.from(context).areNotificationsEnabled(); if (enabled) { notificationManager.notify(notifyId, notification); } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS) .putExtra(Settings.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); } else { Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClassName("com.android.settings","com.android.settings.InstalledAppDetails"); intent.putExtra("com.android.settings.ApplicationPkgName", getPackageName()); startActivity(intent); } }
-
2.2: 事情还没结束:在oppo手机上发现即使引导用户打开了通知开关但是单个channel默认还是关闭的,需要两个开关都开启才能正常使用。而其他手机在开启通知时自动开启channelId的通知开关。
oppo手机允许通知后仍需进一步操作
再改改:
Build.VERSION_CODES.O => 26 Build.VERSION_CODES.LOLLIPOP => 21 Build.VERSION_CODES.GINGERBREAD => 9 NotificationChannel channelUsed = manager.getNotificationChannel(notification.getChannelId()); boolean isNotifyEnable = NotificationManagerCompat.from(this).areNotificationsEnabled(); boolean isChannelEnable = true; if (Build.VERSION.SDK_INT >= 26) { isChannelEnable = channelUsed.getImportance() != NotificationManager.IMPORTANCE_NONE; } if (isNotifyEnable && isChannelEnable) { manager.notify(notifyId, notification); } else if (!isNotifyEnable) { if (Build.VERSION.SDK_INT >= 26) { startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS) .putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName())); }else if (Build.VERSION.SDK_INT >= 21) { startActivity(new Intent("android.settings.APP_NOTIFICATION_SETTINGS") .putExtra("android.provider.extra.APP_PACKAGE", getPackageName())); } else if (Build.VERSION.SDK_INT >= 9) { 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); } else { Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails"); intent.putExtra("com.android.settings.ApplicationPkgName", getPackageName()); startActivity(intent); } }else{ 用户打开了通知开关,但是没有允许当前channel的通知,根据通知重要性自行决定如何提示用户 }
- 注意点1:查看安卓5.0(api21)与8.0(api26)的源码可知
Settings. ACTION_APP_NOTIFICATION_SETTINGS
的值其实就是"android.settings. APP_NOTIFICATION_SETTINGS"
,只是在安卓8.0以下这个常量对开发者是隐藏的。因此上述代码如想简化可省略if(Build.VERSION.SDK_INT >= 26){...}
这一块。 - 注意点2:
NotificationChannel channelUsed
在构建通知的时候使用的NotificationChannel
和真实使用的NotificationChannel
可能是不同的。因为createNotificationChannels
在service层实现的时候保存了一个record缓存,notify的时候是从record取的channel对象,此时importance的值极可能已经产生了变化。因此需要使用manager.getNotificationChannel(notification.getChannelId())
返回的NotificationChannel
对象 - 小插曲:测试时发现vivo X21有两个通知管理页面:
明显右边很漂酿啊有木有 vivo的设计很符合我的审美哈哈,试试改下跳转,左边的太丑了不要了。通过shell 命令
dumpsys activity | grep -i run
找到落地页:com.android.systemui / com.vivo.systemui.statusbar.notification.settings.channels.NotificationSettingsActivity
先不管参数跑下试试,不出所料崩了,提示Permission Denial: starting Intent{...}not exported from uid 10023
。activity没有设置exported属性为true,无法访问。暂时想不到怎么暴力破解...
- 注意点1:查看安卓5.0(api21)与8.0(api26)的源码可知
-
-
3:targetSdkVersion<26时的机型适配
- 3.1: 即便你的
compileSdkVersion
小于26,在oppo/vivo的高版本系统上,通知仍然默认是关闭的。也就是说:在目前安卓7.0/8.0占据主流的情况下通知栏适配是 必须 要做的适配。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省略
- 3.1: 即便你的
网友评论