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)的对比一下,可以看到,百度地图下面多了个类别,有百度地图和未分类两类,这两个就是两个渠道,其中"中"和"低"代表当前渠道消息的重要等级,代码可以初始设置,用户可以点进去干预设置,并且可以单独关闭某一个渠道的消息,后面以用户的设置为主。看下爱奇艺的设置下面东西更多了,有游戏或应用下载、聊天消息推送消息等,这些是开发者创建的渠道组,组下面的是真正的渠道,开发者可以创建一些渠道组为渠道归类,渠道组下面至少包含一个渠道,否则不显示。其中渠道组不是必须的,但是渠道是必须要有的,没有创建渠道组默认渠道放在一个叫类别的渠道组下,如百度地图那样。
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文档
调用上面代码后会渠道就会被创建成功,在设置界面如下:
/*
* 简单的发送通知
*/
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以下会忽略。
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内和其他渠道组唯一。通过上面代码就创建了一个叫聊天消息的渠道组,并将新聊天消息这个渠道放到聊天消息渠道组里。执行上面代码,查看设置页如下:
渠道组仅用于展示,发送通知和上面一样。
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);
}
开发者可以删除一个或一个渠道组(多个渠道),不过删除之后会在设置页显示已删除的渠道数,非常不美观。谨慎删除。
/*
* 判断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);
}
@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);
}
}
/**
* 发送通知
*
* @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);
}
上面使用重复代码太多了,不方便使用,下面简单封装一下
/**
* 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