Android 8.0 通知适配

下面说一下Android O (8.0)(API 26)通知的相关适配

一、分析

Android O 之前打开一个App的设置的通知是这样的

Android 8.0 通知适配_第1张图片

发送一条通知通过下面代码

/*
* 简单的发送通知
*/
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)的对比一下,可以看到,百度地图下面多了个类别,有百度地图和未分类两类,这两个就是两个渠道,其中"中"和"低"代表当前渠道消息的重要等级,代码可以初始设置,用户可以点进去干预设置,并且可以单独关闭某一个渠道的消息,后面以用户的设置为主。看下爱奇艺的设置下面东西更多了,有游戏或应用下载、聊天消息推送消息等,这些是开发者创建的渠道组,组下面的是真正的渠道,开发者可以创建一些渠道组为渠道归类,渠道组下面至少包含一个渠道,否则不显示。其中渠道组不是必须的,但是渠道是必须要有的,没有创建渠道组默认渠道放在一个叫类别的渠道组下,如百度地图那样。

Android 8.0 通知适配_第2张图片

二、适配

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文档

调用上面代码后会渠道就会被创建成功,在设置界面如下:

Android 8.0 通知适配_第3张图片

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内和其他渠道组唯一。通过上面代码就创建了一个叫聊天消息的渠道组,并将新聊天消息这个渠道放到聊天消息渠道组里。执行上面代码,查看设置页如下:

Android 8.0 通知适配_第4张图片

渠道组仅用于展示,发送通知和上面一样。

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);
}

开发者可以删除一个或一个渠道组(多个渠道),不过删除之后会在设置页显示已删除的渠道数,非常不美观。谨慎删除。

Android 8.0 通知适配_第5张图片

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每个厂商可能显示都不一样。

Android 8.0 通知适配_第6张图片

默认发送一条通知就会显示角标,但是开发者可以关闭,在创建渠道前调用 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、截图

Android 8.0 通知适配_第7张图片

Android 8.0 通知适配_第8张图片

四、代码封装

上面使用重复代码太多了,不方便使用,下面简单封装一下

/**
 * 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开发,Android,8.0,通知栏,Notification,Android,8.0通知栏适配,渠道)