/   今日科技快讯   /

据国家企业信用信息公示系统的披露,苏宁控股集团股东张近东、张康阳及南京润贤企业管理中心(有限合伙)已将公司全部股权出质给淘宝(中国)软件有限公司。股权出质登记日期为2020年12月4日,合计出质股权数额10亿元人民币,与苏宁控股集团的注册资本金额等同。

/   作者简介   /

明天就是周六啦,祝大家周末愉快!

本篇文章来自椎锋陷陈的投稿,分享了如何规范通知渠道,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章!

椎锋陷陈的博客地址:

https://www.jianshu.com/u/9bfd80e684ad

/   前言   /

你有强迫症吗?

作为用户的你,有没有试过这样的经历,常常会被一款APP的频繁推送烦扰,但又因为怕错过其中的重要信息,而不敢一刀切地将该APP的通知功能禁用掉?

而作为开发者的你,又有没有遇到这样的需求,要求应用内的有些通知能让用户立即看到(如@提醒消息),而有些通知却只要求佛性地在抽屉通知栏躺着(如下载进度通知)?

如果你有以上的痛苦,那么这篇文章就是你的解苦良药。

假如给你展示以下两张图,哪一张会让你看起来感觉更舒爽?

以上「类别」下的选项即是Android 8引入的通知渠道(Notification Channel)。

做过Android 8系统适配工作的人可能知道,以Android 8(API 级别 26)及更高版本为平台的应用,如果没有为所有通知分配渠道,则会显示不了通知。然而,大部分开发者都是知其然而不知其所以然,并不清楚Android 8引入这个机制的初衷是什么,于是就出现了图二这种莫名其妙的表现。也难怪,「通知渠道」这个名称确实不太好理解,用「通知场景」来描述,可能就清晰准确得多。

/   场景剖析   /

什么是通知场景呢?以一款即时通讯APP为例,可能包含系统通知、聊天消息、@提醒消息、音视频通话等多种场景类型下的消息,不同类型的消息要求对用户的提醒程度不一样,具体就体现在对用户视觉、听觉上的干扰程度上。

举个栗子,对于系统通知类的推送(比如谁加了我好友),可能并不需要用户立即处理,所以只需要在状态栏显示一个小图标,并在抽屉式通知栏显示一条通知,让用户知道有这件事即可。

而对于聊天消息,由于可能包含用户关心的内容,除了以上两个手段,常常还需要辅以提示音和震动以加强提醒。

至于@提醒消息和音视频消息,一般都是有针对性地推送,是需要用户立即处理的,要求能以浮动通知的形式显示,并且此时如果处于息屏状态,还需要能够唤醒屏幕,并在锁屏页面显示通知,音视频消息还可能需要自定义提示音以及持续震动,才能达到强提醒的目的。

此处总结为一张表格:

那么,具体如何用通知渠道来实现呢?接下来,就让我们在本文的引领下,一扫之前应用内通知混乱无章的局面,重新整理出应用通知渠道的规范吧。

/   知识储备   /

NotificationChannel

代表一个「通知渠道」对象,该类的构造函数中包含了3个最重要的属性,即:唯一渠道 ID、用户可见名称和重要性级别。

  • 唯一渠道 ID

通知渠道的唯一标识,我们可以通过该标识查询特定的渠道设置,打开通知渠道的系统设置或删除特定的通知渠道。

  • 用户可见名称

即打开通知管理界面后最能直观看到的通知渠道名称。

  • 重要性级别

渠道重要性会影响在渠道中发布的所有通知的干扰级别,即上文所提及的提示音、状态栏显示、抽屉式通知栏显示以及浮动通知等。

如需支持 Android 7.1(API 级别 25)或更低版本的平台,还需调用NotificationCompat 类中的 setPriority()方法,针对每条通知设置对应的优先级常量。

渠道重要性级别及其对应的干扰级别如下:

需要注意的是,当我们创建好通知渠道并提交后,便无法再更改通知行为,此时用户拥有完全控制权,用户可以随时可以在通知设置页更改他们对应用渠道的偏好设置。

我们能做的,仅仅包括以下几个方面:

  • 更改现有渠道的名称和说明

  • 读取现有通知渠道的不良设置,从而建议用户更改该设置

  • 删除现有通知渠道

有了这些知识储备后,我们就可以着手开始代码实践了。

/   代码实践   /

首先,我们定义一个数据类Channel,用以描述某个通知渠道的具体设置:

/*** 通知渠道*/
data class Channel(val channelId: String,      // 唯一渠道IDval name: CharSequence,     // 用户可见名称val importance: Int,        // 重要性级别val description: String? = null,      // 描述@NotificationCompat.NotificationVisibilityval lockScreenVisibility: Int = NotificationCompat.VISIBILITY_SECRET,        // 锁定屏幕公开范围val vibrate: LongArray? = null,      // 震动模式val sound: Uri? = null               // 声音
)

为什么不直接用NotificationChannel?

因为NotificationChannel是API 26才引入的对象,参考NotificationCompat类的设计,为了对客户端屏蔽版本兼容的细节,所以采用了自定义的数据类 。

接着,我们定义一个通知工具类,用以封装创建通知、创建通知渠道、发布通知、取消通知等公用方法以隔离具体业务,并处理好版本兼容工作。其中,createNotificationBuilder方法会返回创建通知的Builder对象,以允许业务方根据需要进一步扩展通知表现形式。

/*** 通知兼容工具类** 本类中的代码使用Android支持库中的NotificationCompatAPI。* 这些API允许您添加仅在较新版本Android上可用的功能,同时仍向后兼容Android4.0(API级别14)。* 但是,诸如内嵌回复操作等部分新功能在较旧版本上会导致发生空操作。*/
class NotificationCompatUtil {companion object {/*** 创建通知* @param context           上下文* @param channel           通知渠道* @param title             标题* @param text              正文文本* @param intent            对点按操作做出响应意图* @return*/fun createNotificationBuilder(context: Context,channel: Channel,title: CharSequence? = null,text: CharSequence? = null,intent: Intent? = null): NotificationCompat.Builder {// 必须先创建通知渠道,然后才能在Android 8.0及更高版本上发布任何通知if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {createChannel(context, channel)}val builder =NotificationCompat.Builder(context, channel.channelId).setPriority(getLowVersionPriority(channel)) // 通知优先级,优先级确定通知在Android7.1和更低版本上的干扰程度。.setVisibility(channel.lockScreenVisibility) // 锁定屏幕公开范围.setVibrate(channel.vibrate) // 震动模式.setSound(channel.sound ?: Settings.System.DEFAULT_NOTIFICATION_URI)    // 声音.setOnlyAlertOnce(true) // 设置通知只会在通知首次出现时打断用户(通过声音、振动或视觉提示),而之后更新则不会再打断用户。// 标题,此为可选内容if (!TextUtils.isEmpty(title)) builder.setContentTitle(title)// 正文文本,此为可选内容if (!TextUtils.isEmpty(text)) builder.setContentText(text)// 设置通知的点按操作,每个通知都应该对点按操作做出响应,通常是在应用中打开对应于该通知的Activity。if (intent != null) {val pendingIntent =PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)builder.setContentIntent(pendingIntent).setAutoCancel(true) // 在用户点按通知后自动移除通知if(NotificationManager.IMPORTANCE_HIGH == channel.importance) builder.setFullScreenIntent(pendingIntent, false)}return builder}/*** 获取低版本的优先级* 要支持搭载 Android 7.1(API 级别 25)或更低版本的设备,* 您还必须使用 NotificationCompat 类中的优先级常量针对每条通知调用 setPriority()。* @param channel* @return*/private fun getLowVersionPriority(channel: Channel): Int {return when (channel.importance) {NotificationManager.IMPORTANCE_HIGH -> NotificationCompat.PRIORITY_HIGHNotificationManager.IMPORTANCE_LOW -> NotificationCompat.PRIORITY_LOWNotificationManager.IMPORTANCE_MIN -> NotificationCompat.PRIORITY_MINelse -> NotificationCompat.PRIORITY_DEFAULT}}/*** 创建通知渠道* <p>* 反复调用这段代码也是安全的,因为创建现有通知渠道不会执行任何操作。* 注意:创建通知渠道后,您便无法更改通知行为,此时用户拥有完全控制权。不过,您仍然可以更改渠道的名称和说明。* @param context 上下文* @param channel 通知渠道*/@RequiresApi(api = Build.VERSION_CODES.O)private fun createChannel(context: Context,channel: Channel) {val notificationChannel =NotificationChannel(channel.channelId, channel.name, channel.importance)notificationChannel.description = channel.description   // 描述notificationChannel.vibrationPattern = channel.vibrate  // 震动模式notificationChannel.setSound(channel.sound ?: Settings.System.DEFAULT_NOTIFICATION_URI, notificationChannel.audioAttributes)    // 声音val notificationManager =context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManagernotificationManager.createNotificationChannel(notificationChannel)}/*** 显示通知*** 请记得保存您传递到 NotificationManagerCompat.notify() 的通知 ID,因为如果之后您想要更新或移除通知,将需要使用这个 ID。* @param context      上下文* @param id           通知的唯一ID* @param notification 通知*/fun notify(context: Context,id: Int,notification: Notification?) {val notificationManager =context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManagernotificationManager.notify(id, notification)}/*** 取消通知* @param context 上下文* @param id      通知的唯一ID*/fun cancel(context: Context, id: Int) {val notificationManager =context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManagernotificationManager.cancel(id)}/*** 取消所有通知* @param context 上下文*/fun cancelAll(context: Context) {val notificationManager =context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManagernotificationManager.cancelAll()}}
}

然后,在业务模块定义一个推送通知管理类,用于管理具体业务场景下的通知,分为两个部分,渠道对象和通知方法。

  • 渠道对象

由于前面在定义Channel类时已经做好部分属性的默认值设置,所以这里我们只需按需配置Channel对象的属性,以这种编码形式能够很直观地将Channel对象映射到通知设置里对应的通知渠道配置。

// PushNotificationHelper.kt/** 通知渠道-聊天消息(重要性级别-高:发出声音) */
private val MESSAGE = NotificationCompatUtil.Channel(channelId = "MESSAGE",name = BaseApplication.getContext().getString(R.string.channel_message),importance = NotificationManager.IMPORTANCE_DEFAULT
)/** 通知渠道-@提醒消息(重要性级别-紧急:发出提示音,并以浮动通知的形式显示 & 锁屏显示 & 振动0.25s )*/
private val MENTION = NotificationCompatUtil.Channel(channelId = "MENTION",name = BaseApplication.getContext().getString(R.string.channel_mention),importance = NotificationManager.IMPORTANCE_HIGH,lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC,vibrate = longArrayOf(0, 250)
)/** 通知渠道-系统通知(重要性级别-中:无提示音) */
private val NOTICE = NotificationCompatUtil.Channel(channelId = "NOTICE",name = BaseApplication.getContext().getString(R.string.channel_notice),importance = NotificationManager.IMPORTANCE_LOW
)/** 通知渠道-音视频通话(重要性级别-紧急:发出提示音,并以浮动通知的形式显示 & 锁屏显示 & 振动4s停2s再振动4s ) */
private val CALL = NotificationCompatUtil.Channel(channelId = "CALL",name = BaseApplication.getContext().getString(R.string.channel_call),importance = NotificationManager.IMPORTANCE_HIGH,lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC,vibrate = longArrayOf(0, 4000, 2000, 4000),sound = Uri.parse("android.resource://" + BaseApplication.getContext().packageName + "/" + R.raw.iphone)
)
  • 通知方法

即具体业务场景下的推送逻辑实现,每种方法都必须指定一个通知渠道,调用NotificationCompatUtil类的createNotificationBuilder方法获取创建通知的Builder之后,可以根据需要丰富通知的表现形式:

// PushNotificationHelper.kt/*** 显示聊天消息* @param context 上下文* @param id      通知的唯一ID* @param title   标题* @param text    正文文本*/
fun notifyMessage(context: Context,id: Int,title: String?,text: String?
) {val intent = Intent(context, MainActivity::class.java)val builder = NotificationCompatUtil.createNotificationBuilder(context,MESSAGE,title,text,intent)// 默认情况下,通知的文字内容会被截断以放在一行。如果您想要更长的通知,可以使用 setStyle() 添加样式模板来启用可展开的通知。builder.setStyle(NotificationCompat.BigTextStyle().bigText(text))NotificationCompatUtil.notify(context, id, buildDefaultConfig(builder));
}/*** 显示@提醒消息* @param context 上下文* @param id      通知的唯一ID* @param title   标题* @param text    正文文本*/
fun notifyMention(context: Context,id: Int,title: String?,text: String?
) {val intent = Intent(context, MainActivity::class.java)val builder = NotificationCompatUtil.createNotificationBuilder(context,MENTION,title,text,intent)// 默认情况下,通知的文字内容会被截断以放在一行。如果您想要更长的通知,可以使用 setStyle() 添加样式模板来启用可展开的通知。builder.setStyle(NotificationCompat.BigTextStyle().bigText(text))NotificationCompatUtil.notify(context, id, buildDefaultConfig(builder));
}/*** 显示系统通知* @param context 上下文* @param id      通知的唯一ID* @param title   标题* @param text    正文文本*/
fun notifyNotice(context: Context,id: Int,title: String?,text: String?
) {val intent = Intent(context, MainActivity::class.java)val builder = NotificationCompatUtil.createNotificationBuilder(context,NOTICE,title,text,intent)NotificationCompatUtil.notify(context, id, buildDefaultConfig(builder));
}/*** 显示音视频通话* @param context 上下文* @param id      通知的唯一ID* @param title   标题* @param text    正文文本*/
fun notifyCall(context: Context,id: Int,title: String?,text: String?
) {val intent = Intent(context, MainActivity::class.java)val builder = NotificationCompatUtil.createNotificationBuilder(context,CALL,title,text,intent)NotificationCompatUtil.notify(context, id, buildDefaultConfig(builder));
}/*** 构建应用通知的默认配置* @param builder 构建器*/
private fun buildDefaultConfig(builder: NotificationCompat.Builder): Notification {builder.setSmallIcon(R.drawable.ic_launcher_foreground)return builder.build()
}

至此,我们便完成了通知方法→渠道对象→通知渠道三者间的映射关系,建立了通知渠道的使用规范,在分别推送了各自渠道下的通知之后,我们可以在通知设置里看到:

具体的Demo已经上传到GitHub(https://github.com/madchan/NotificationChannelLib)

/   总结   /

在我看来,规范使用「通知渠道」的好处可以概括为:

更细粒度地划分应用内通知的场景,可以单独控制每种场景对用户的干扰程度,降低对用户的打扰,从而提高用户体验。

幸运的是,很多APP已经意识到了这个好处并付诸实践,但仍有更多APP的通知管理仍处于很混乱的状态,因此希望这部文章能够起到一定指导作用 ,帮助他们建立规范。

推荐阅读:

我的新书,《第一行代码 第3版》已出版!

用MotionLayout实现这些不可思议的效果

Android富文本开发,从0到1!

欢迎关注我的公众号

学习技术或投稿

长按上图,识别图中二维码即可关注

规范你的Android应用通知渠道相关推荐

  1. android o preview 3,Android O Preview 之 通知渠道(Notification Channels)

    介绍 Android O 引入了 通知渠道(Notification Channels),以提供统一的系统来帮助用户管理通知,如果是针对 android O 为目标平台时,必须实现一个或者多个通知渠道 ...

  2. Android开发笔记(一百六十八)为应用绑定通知渠道并展示消息角标

    为了分清消息通知的轻重缓急,从Android8开始新增了通知渠道,并且必须指定通知渠道才能正常推送消息.一个应用允许拥有多个通知渠道,每个渠道的重要性各不相同,有的渠道消息在通知栏被折叠成小行,有的渠 ...

  3. Android 8.0 创建管理通知渠道Notification

    从Android8.0(API26)开始,所有的通知必须分配一个渠道.每一个渠道,你都可以设置渠道中所有通知的视觉和听觉行为.然后,用户能够随意修改这些设置来决定通知的行为. 在用户界面渠道显示为 & ...

  4. Android Studio App开发之通知渠道NotificationChannel及给华为、小米手机桌面应用添加消息数量角标实战(包括消息重要级别的设置 附源码)

    需要全部源码或运行有问题请点赞关注收藏后评论区留言~~~ 一.通知渠道NtoificationChannel 为了分清消息通知的轻重缓急,Android8.0新增了通知渠道,并且必须指定通知渠道才能正 ...

  5. 适配 通知 Notification 通知渠道 前台服务 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  6. Android——横幅通知

    横幅通知,也称为提醒式通知,效果如下图: 这个效果在QQ,微信,钉钉等一些主流的App当中,大家一定很熟悉,今天就来说说如何实现. 可能会触发提醒式通知的条件有如下3种: 用户的Activiity处于 ...

  7. android oreo 老机型,Android Oreo 通知新特性,这坑老夫先踩了

    前些天性致脖脖地点进入了developer.android.com,想看下通知这块内容,你懂的.首先映入眼帘的就是下面这玩意儿,翻译速度阔以哦!!! image 通知渠道?啥玩意儿,啊,走过路过,千万 ...

  8. Android发送通知——通知栏(Notification)

    Android发送通知--通知栏(Notification) 通知是指 Android 在您应用的界面之外显示的消息,旨在向用户提供提醒.来自他人的通信信息或您应用中的其他实时信息.在发出一条通知后, ...

  9. android系统 通知管理,Android的通知系统

    Android的通知系统 默认分类 | 2015-07-07 08:21:24 | 阅读 1581 次 | 评论(0) : 将应用程序的一些重要信息通知给用户. 1.Toast 形式:一般在界面下半部 ...

最新文章

  1. 【重建】从FJOI2016一试谈起
  2. 大厂围城:千辛万苦杀进来,为何他们选择出逃?
  3. 【 C 】结构的自引用
  4. Web渗透测试中常见逻辑漏洞解析与实战
  5. java单纯形法_单纯形法 - fjzzq2002 - 博客园
  6. C++ opengl 深度缓冲区
  7. 德国黑客使用租用的计算机资源攻击散列算法
  8. 给RABBITMQ发送消息时,设置请求头HEADER
  9. 自定义控件的构建(6)
  10. 使用opencv实现简单的人脸识别
  11. 机器学习——sklearn实现决策树(隐形眼镜预测和鸢尾花分类)
  12. mysql如何提高查询效率_MySQL调优系列——如何提高MySQL的查询效率
  13. 美国公布自动驾驶新政AV4.0;微软Access数据库出现漏洞,或致8.5万家企业面临风险;苹果谈论隐私问题……...
  14. 怎样把pdf格式转换成jpg
  15. 【MySQL】根据数据表中日期字段查询某个月每一天的数据量?查询数据表中所有日期每天的数据量?近三天每天数据量?
  16. Kerberos学习(四)
  17. DS1042C数字示波器的波形截图流程
  18. java压缩源代码_压缩图片大小(Java源码)
  19. 中国医科大学网络教育学院试卷计算机,中国医科大学网络教育学院试卷
  20. URP Bokeh DOF 分析

热门文章

  1. 大一科创项目课题计算机电子,在工大,大一科创和交流交换原来得这样做……...
  2. 构建系统软件三步走,这些书你不可错过!
  3. 2017java考证_2017年Java认证考试试题
  4. 【BLE】CC2541之硬件IIC(TMP102温度传感器)
  5. myeclipse的JS出现红叉叉解决方法
  6. 微信开发者工具 微信小程序中调试器console界面不显示跑出的代码结果的问题解决
  7. release debug
  8. C#自定义控件的创建
  9. 二台苹果电脑如何用网线直接连接
  10. C++11特性(01)auto关键字