下面说一下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

更多相关文章

  1. 浅析Android中的消息机制-解决:Only the original thread that cr
  2. Android(安卓)studio3.x 多渠道打包apk
  3. Android异步消息机制之Handler
  4. 【Android(安卓)开发教程】Toast通知
  5. Android设置通知栏/状态栏透明改变通知栏颜色和app最上部分颜色
  6. Android开发之消息处理机制(一)——Handler
  7. Gradle多渠道打包
  8. Titanium 使用刘明星的Jpush module做android端的消息推送
  9. Android(安卓)通知Notification的两种实现方法

随机推荐

  1. Android标题栏沉浸效果
  2. Android(安卓)SQLite数据库 《第一行代码
  3. Android(安卓)控件七 ImageView 控件
  4. Android(安卓)Sensor
  5. android stadio多渠道打包(一分钟搞定)
  6. Android系统服务-WindowManager
  7. Android的MediaRecorder架构介绍
  8. Android加载/处理超大图片神器!Subsamplin
  9. Android(安卓)Gradle权威指南
  10. Android关于短信加密