Android(安卓)8.0 通知适配
下面说一下Android O (8.0)(API 26)通知的相关适配
一、分析
Android O 之前打开一个App的设置的通知是这样的
发送一条通知通过下面代码
/** 简单的发送通知*/private void showNotification() { NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); Notification notification = new NotificationCompat.Builder(this) .setSmallIcon(R.drawable.push) .setContentTitle("title") .setContentText("content") .build(); notificationManager.notify(1, notification);}
在Android O 之前调用上面代码App是可以正常收到一条通知的,但是在Android O之后调用上面代码并不会收到通知,会打印出下面的log信息
No Channel found for pkg=com.bill.notificationtest, channelId=null, id=1, tag=null, opPkg=com.bill.notificationtest, callingUid=10246, userId=0, incomingUserId=0, notificationUid=10246, notification=Notification(channel=null pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x0 color=0x00000000 vis=PRIVATE)
从Android8.0(API26)开始,Google 规定所有的通知必须分配一个渠道。每一个渠道,你都可以设置渠道中所有通知的视觉和听觉行为。然后,用户能够随意修改这些设置来决定通知的行为。
适配Android O 通知需要将App的targetSdkVersion修改为26及以上,然后在Android 8.0以上的机型就可以使用,在Android 8.0以下的机型还是按照原有的规则。
下面看一下国内已经适配了Android O的App,下面为百度地图和爱奇艺的设置通知页,和上面头条(未适配Android O)的对比一下,可以看到,百度地图下面多了个类别,有百度地图和未分类两类,这两个就是两个渠道,其中"中"和"低"代表当前渠道消息的重要等级,代码可以初始设置,用户可以点进去干预设置,并且可以单独关闭某一个渠道的消息,后面以用户的设置为主。看下爱奇艺的设置下面东西更多了,有游戏或应用下载、聊天消息推送消息等,这些是开发者创建的渠道组,组下面的是真正的渠道,开发者可以创建一些渠道组为渠道归类,渠道组下面至少包含一个渠道,否则不显示。其中渠道组不是必须的,但是渠道是必须要有的,没有创建渠道组默认渠道放在一个叫类别的渠道组下,如百度地图那样。
二、适配
1、创建渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { String channelId = "channel_chat"; String channelName = "新聊天消息"; int importance = NotificationManager.IMPORTANCE_HIGH; NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationChannel channel = new NotificationChannel(channelId, channelName, importance); notificationManager.createNotificationChannel(channel);}
创建渠道就上面几行代码,可以写在任何地方,需要在发送通知前调用,一个渠道Id系统只会创建一次,以后不会重复创建。创建渠道首先需要判断版本,如果不判断,在Android 8.0 以前的机型调用将会找不到Api会造成崩溃。然后创建渠道需要三个必填参数,channelId是渠道Id,开发者自己定义,在当前App内保证和其他渠道信息唯一就好了,后面发送消息也是根据渠道Id发送的。channelName是渠道名称,是给用户看的,文档建议最长为40个字符,否则可能被截断,例如上面百度地图App里的百度地图和未分类。importance是渠道消息的重要等级,如上面百度地图的中和低,用户可以修改等级,具体取值如下:
IMPORTANCE_HIGH:紧急。有提示音和震动,在状态栏显示,并会悬浮到App上。
IMPORTANCE_DEFAULT:高。有提示音和震动,在状态栏显示。
IMPORTANCE_LOW:中。没有提示音和震动,在状态栏显示。
IMPORTANCE_MIN:低。没有提示音和震动,不在状态栏显示,折叠在状态栏二级菜单中,下拉可以看到。
IMPORTANCE_NONE:关闭通知。
NotificationChannel 还有几个设置项,一般都不修改,须在createNotificationChannel()方法前调用。如下:
/** 为当前渠道添加一个描述信息,用户在点击渠道进入详情页,会在最下面看到* 文档提示最长为300个字符,否则可能被截断*/setDescription(String description);/** 设置此渠道所属的渠道组,仅用于展示。即不可以向一个组发送通知,可以删除一组渠道。*/setGroup(String groupId);/** 设置App在桌面上显示Icon上是否显示角标,true:显示(default)*/setShowBadge(boolean showBadge);/** 设置通知的提示音,默认系统的,用户可修改* 注意只有在重要等级IMPORTANCE_DEFAULT和IMPORTANCE_HIGH才有提示音*/setSound(Uri sound, AudioAttributes audioAttributes);/** 设置通知的指示灯颜色,手机需支持*/setLightColor(int argb);/** 设置通知的震动模式*/setVibrationPattern(long[] vibrationPattern);/** 绕过免打扰模式* 注:Only modifiable by the system and notification ranker.(只能被系统和通知服务修改)*/setBypassDnd(boolean bypassDnd);/** 是否在锁定屏幕上显示通知* 注:Only modifiable by the system and notification ranker.(只能被系统和通知服务修改)*/setLockscreenVisibility(int lockscreenVisibility);
NotificationChannel文档
调用上面代码后会渠道就会被创建成功,在设置界面如下:
2、发送通知
/** 简单的发送通知*/private void showNotification() { String channelId = "channel_chat"; NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); Notification notification = new NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.push) .setContentTitle("title") .setContentText("content") .build(); notificationManager.notify(1, notification);}
发送通知只在创建Builder时多了一个channelId参数,在Android 8.0及以上机型会根据渠道Id发送通知,Andoird 8.0以下会忽略。
3、创建渠道组
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { String groupId = "group_chat"; String groupName = "聊天消息"; NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationChannelGroup group = new NotificationChannelGroup(groupId, groupName); notificationManager.createNotificationChannelGroup(group); String channelId = "channel_chat"; String channelName = "新聊天消息"; int importance = NotificationManager.IMPORTANCE_HIGH; NotificationChannel channel = new NotificationChannel(channelId, channelName, importance); channel.setGroup(groupId); notificationManager.createNotificationChannel(channel);}
创建渠道组只需要一个groupId必填参数,并保证在App内和其他渠道组唯一。通过上面代码就创建了一个叫聊天消息的渠道组,并将新聊天消息这个渠道放到聊天消息渠道组里。执行上面代码,查看设置页如下:
渠道组仅用于展示,发送通知和上面一样。
4、删除渠道和组
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { notificationManager.deleteNotificationChannel(channelId);}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { notificationManager.deleteNotificationChannelGroup(groupId);}
开发者可以删除一个或一个渠道组(多个渠道),不过删除之后会在设置页显示已删除的渠道数,非常不美观。谨慎删除。
5、通知关闭
/** 判断App通知是否打开(总开关)* true:开*/public boolean areNotificationsEnabled() { NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context); return notificationManagerCompat.areNotificationsEnabled();}/** 判断当前渠道通知是否打开* true:开*/@RequiresApi(api = Build.VERSION_CODES.O)public boolean areChannelsEnabled(@NonNull String channelId) { NotificationChannel notificationChannel = notificationManager.getNotificationChannel(channelId); if (notificationChannel != null && notificationChannel.getImportance() == NotificationManager.IMPORTANCE_NONE) { return false; } return true;}/** 跳转到渠道设置页*/public void gotoChannelSetting(@NonNull String channelId, @NonNull Context context) { Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName()); intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId); context.startActivity(intent);}
注:当通知总开关被关闭时即areNotificationsEnabled()返回false时,渠道开关即areChannelsEnabled("")可能是true。所以判断渠道版本开关前应该先判断总开关。
三、通知角标
App收到通知后会在桌面icon右上角显示一个小圆圈,不显示通知数量,长按弹出详细内容显示数量和内容,Android每个厂商可能显示都不一样。
默认发送一条通知就会显示角标,但是开发者可以关闭,在创建渠道前调用 NotificationChannel 的 setShowBadge方法,传入 false 即可关闭某个渠道的角标。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { String channelId = "channel_chat"; String channelName = "新聊天消息"; int importance = NotificationManager.IMPORTANCE_HIGH; NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationChannel channel = new NotificationChannel(channelId, channelName, importance); channel.setShowBadge(false); // 关闭角标 notificationManager.createNotificationChannel(channel);}
发送通知时可以设置角标数量,通过调用 NotificationCompat.Builder 的 setNumber(number) 方法传入数量,默认不传每次加1,传入数字后累加number。
private void showNotification() { String channelId = "channel_chat"; NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); Notification notification = new NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.push) .setContentTitle("title") .setContentText("content") .setNumber(2) // 设置角标数量 .build(); notificationManager.notify(1, notification);}
四、完整示例
1、在Activity的onCreate中添加如下代码
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 聊天消息 String groupId_1 = "group_chat"; String groupName_1 = "聊天消息"; NotificationChannelGroup group = new NotificationChannelGroup(groupId_1, groupName_1); notificationManager.createNotificationChannelGroup(group); String channelId1 = "channel_chat"; String channelName1 = "新聊天消息"; int importance1 = NotificationManager.IMPORTANCE_HIGH; NotificationChannel channel1 = new NotificationChannel(channelId1, channelName1, importance1); channel1 .setDescription("个人或群组发来的聊天消息"); channel1.setGroup(groupId_1); notificationManager.createNotificationChannel(channel1); // 下载消息 String groupId2 = "group_download"; String groupName2 = "下载消息"; NotificationChannelGroup group2 = new NotificationChannelGroup(groupId2, groupName2); notificationManager.createNotificationChannelGroup(group2); String channelId_2_1 = "channel_download_complete"; String channelName_2_1 = "下载完成"; int importance_2_1 = NotificationManager.IMPORTANCE_LOW; NotificationChannel channel_2_1 = new NotificationChannel(channelId_2_1, channelName_2_1, importance_2_1); channel_2_1.setGroup(groupId2); notificationManager.createNotificationChannel(channel_2_1); String channelId_2_2 = "channel_download_error"; String channelName_2_2 = "下载失败"; int importance_2_2 = NotificationManager.IMPORTANCE_DEFAULT; NotificationChannel channel_2_2 = new NotificationChannel(channelId_2_2, channelName_2_2, importance_2_2); channel_2_2.setGroup(groupId2); notificationManager.createNotificationChannel(channel_2_2); // 未分类 String channelId_3 = "channel_other"; String channelName_3 = "未分类"; int importance_3 = NotificationManager.IMPORTANCE_MIN; NotificationChannel channel_3 = new NotificationChannel(channelId_3, channelName_3, importance_3); channel_3.setShowBadge(false); notificationManager.createNotificationChannel(channel_3); } }
2、发送通知
/** * 发送通知 * * @param channelId 渠道Id,按渠道发送通知 */ private void sendNotification(@NonNull String channelId) { NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); Notification notification = new NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.push) .setContentTitle("title") .setContentText("content") .build(); int notifyId = new Random().nextInt(50000); notificationManager.notify(notifyId, notification); }
3、截图
四、代码封装
上面使用重复代码太多了,不方便使用,下面简单封装一下
/** * Created by Bill on 2018/10/22. * 通知管理类 */public class NotifyManager { private Context context; private NotificationManager notificationManager; private Random random; public NotifyManager(@NonNull Context context) { this.context = context.getApplicationContext(); init(); } private void init() { notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); random = new Random(); } /** * 创建渠道 * * @param channelEntity 渠道消息 */ public void createNotificationChannel(ChannelEntity channelEntity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createNotificationChannel(channelEntity, null); } } /** * 创建渠道组和一个渠道 * * @param groupId * @param groupName * @param channel */ public void createNotificationGroupWithChannel(@NonNull String groupId, @Nullable String groupName, @NonNull ChannelEntity channel) { ArrayList channelList = new ArrayList<>(); channelList.add(channel); createNotificationGroupWithChannel(groupId, groupName, channelList); } /** * 创建渠道组和一组渠道 * * @param groupId * @param groupName * @param channelList */ public void createNotificationGroupWithChannel(@NonNull String groupId, @Nullable String groupName, @NonNull ArrayList channelList) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { if (!TextUtils.isEmpty(groupId)) { createNotificationGroup(groupId, groupName); } for (ChannelEntity channel : channelList) { createNotificationChannel(channel, groupId); } } } /** * 创建渠道,并创建组 * * @param channelEntity * @param groupId */ @RequiresApi(api = Build.VERSION_CODES.O) private void createNotificationChannel(@NonNull ChannelEntity channelEntity, @Nullable String groupId) { NotificationChannel channel = new NotificationChannel(channelEntity.getChannelId(), channelEntity.getChannelName(), channelEntity.getImportance()); channel.setShowBadge(channelEntity.isShowBadge()); if (!TextUtils.isEmpty(channelEntity.getDescription())) channel.setDescription(channelEntity.getDescription()); if (!TextUtils.isEmpty(groupId)) channel.setGroup(groupId); notificationManager.createNotificationChannel(channel); } /** * 创建渠道组 * * @param groupId * @param groupName */ @RequiresApi(api = Build.VERSION_CODES.O) private void createNotificationGroup(@NonNull String groupId, @Nullable String groupName) { NotificationChannelGroup group = new NotificationChannelGroup(groupId, groupName); notificationManager.createNotificationChannelGroup(group); } /** * 删除渠道 * * @param channelId */ public void deleteNotificationChannel(@NonNull String channelId) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { notificationManager.deleteNotificationChannel(channelId); } } /** * 删除组 * * @param groupId */ public void deleteNotificationChannelGroup(@NonNull String groupId) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { notificationManager.deleteNotificationChannelGroup(groupId); } } /** * 发送通知 * * @param notification 通知具体内容 * @return 通知Id */ public int notifyNotify(@NonNull Notification notification) { int notifyId = getRandomId(); return notifyNotify(notifyId, notification); } /** * 发送通知 * * @param notifyId 通知Id * @param notification 通知具体内容 * @return */ public int notifyNotify(int notifyId, @NonNull Notification notification) { notificationManager.notify(notifyId, notification); return notifyId; } /** * 关闭状态栏通知的显示 * * @param notifyId 通知Id */ public void cancelNotify(int notifyId) { notificationManager.cancel(notifyId); } /** * 默认设置,调用方可以添加和修改 * * @param channelId * @return */ public NotificationCompat.Builder getDefaultBuilder(@NonNull String channelId) { NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId); builder.setSmallIcon(R.drawable.push) .setColor(Color.parseColor("#E92110")); return builder; } /** * 检查当前渠道的通知是否可用,Android O及以上版本调用 * * 注:areNotificationsEnabled()返回false时,即当前App通知被关时,此方法仍可能返回true, * * @param channelId 渠道Id * @return false:不可用 */ @RequiresApi(api = Build.VERSION_CODES.O) public boolean areChannelsEnabled(@NonNull String channelId) { NotificationChannel notificationChannel = notificationManager.getNotificationChannel(channelId); if (notificationChannel != null && notificationChannel.getImportance() == NotificationManager.IMPORTANCE_NONE) { return false; } return true; } /** * 检查通知是否可用 * * @return false:不可用 */ public boolean areNotificationsEnabled() { NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context); return notificationManagerCompat.areNotificationsEnabled(); } /** * 调转到渠道设置页 * * @param channelId */ public void gotoChannelSetting(@NonNull String channelId) { Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName()); intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId); context.startActivity(intent); } /** * Generate a random integer * * @return int, [0, 50000) */ private int getRandomId() { return random.nextInt(50000); }}
@IntDef({ImportanceType.IMPORTANCE_NONE, ImportanceType.IMPORTANCE_MIN, ImportanceType.IMPORTANCE_LOW, ImportanceType.IMPORTANCE_DEFAULT, ImportanceType.IMPORTANCE_HIGH})public @interface ImportanceType { int IMPORTANCE_NONE = 0; int IMPORTANCE_MIN = 1; int IMPORTANCE_LOW = 2; int IMPORTANCE_DEFAULT = 3; int IMPORTANCE_HIGH = 4;}
public class ChannelEntity { private String channelId; // 渠道Id private String channelName; // 渠道名称 private int importance; // 重要等级 private String description; // 描述 private boolean showBadge = true; // 是否显示icon角标 public ChannelEntity(@NonNull String channelId, @NonNull String channelName, @ImportanceType int importance) { this.channelId = channelId; this.channelName = channelName; this.importance = importance; } public String getChannelId() { return channelId; } public String getChannelName() { return channelName; } public int getImportance() { return importance; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public boolean isShowBadge() { return showBadge; } public void setShowBadge(boolean showBadge) { this.showBadge = showBadge; }}
调用如下:
private NotifyManager notifyManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); notifyManager = new NotifyManager(this); ChannelEntity chatChannel = new ChannelEntity(Constants.CHANNEL_CHAT, "新聊天消息", ImportanceType.IMPORTANCE_HIGH); chatChannel.setDescription("个人或群组发来的聊天消息"); notifyManager.createNotificationGroupWithChannel(Constants.GROUP_CHAT, "聊天消息", chatChannel); ArrayList channelEntityArrayList = new ArrayList<>(); ChannelEntity downloadCompleteChannel = new ChannelEntity(Constants.CHANNEL_DOWNLOAD_COMPLETE, "下载完成", ImportanceType.IMPORTANCE_LOW); downloadCompleteChannel.setDescription("下载完成后通知栏显示"); channelEntityArrayList.add(downloadCompleteChannel); ChannelEntity downloadProgressChannel = new ChannelEntity(Constants.CHANNEL_DOWNLOAD_ERROR, "下载失败", ImportanceType.IMPORTANCE_DEFAULT); downloadProgressChannel.setDescription("下载出现问题,下载失败"); channelEntityArrayList.add(downloadProgressChannel); notifyManager.createNotificationGroupWithChannel(Constants.GROUP_DOWNLOAD, "下载消息", channelEntityArrayList); ChannelEntity otherChannel = new ChannelEntity(Constants.CHANNEL_OTHER, "未分类", ImportanceType.IMPORTANCE_MIN); otherChannel.setShowBadge(false); notifyManager.createNotificationChannel(otherChannel); }
发送通知:
/** * 发送通知 * * @param channelId 渠道Id,按渠道发送通知 */ private void sendNotification() { NotificationCompat.Builder builder = notifyManager.getDefaultBuilder(Constants.CHANNEL_DOWNLOAD_COMPLETE); builder.setContentTitle("下载完成"); builder.setContentText("下载完成,可在我的下载中查看"); Notification notification = builder.build(); notifyManager.notifyNotify(notification); }
完整代码
参考链接:
https://blog.csdn.net/guolin_blog/article/details/79854070
http://blog.skymxc.com/2018/04/27/CreateAndManageNotificationChannel/
https://www.jianshu.com/p/f85ef58edf63
更多相关文章
- 浅析Android中的消息机制-解决:Only the original thread that cr
- Android(安卓)studio3.x 多渠道打包apk
- Android异步消息机制之Handler
- 【Android(安卓)开发教程】Toast通知
- Android设置通知栏/状态栏透明改变通知栏颜色和app最上部分颜色
- Android开发之消息处理机制(一)——Handler
- Gradle多渠道打包
- Titanium 使用刘明星的Jpush module做android端的消息推送
- Android(安卓)通知Notification的两种实现方法