android 扫盲 - Notification

通知 Notification 我是好久没用过了,说实话都忘的一干二净了,记得前几次用的时候都是现 baidu 的,baidu 几次过后依然是没啥印象,如此和没学何异。所以这笔记该自己写就自己写,不是为了给别人看,而是给自己看,梳理脑中 Notification 的知识点以方便记忆,不梳理的记忆如同杂草,不但没好处还有害


纵览 Notification API

官方代码,无论如何代码结构分层总是做的很好的,抓住了业功能分层就能了解全貌了

Notification 的核心 API:

很简单,很经典的代码机构,不用多说大家都看得懂,都明白其中的意思,麻烦一些的是适配:

  • 4.1 - 4.1 之后 Notification 有些变化,官方提供了 NotificationCompat 兼容老版本
  • 8.0 - 同一个 app 的同送可能有很多,官方让加上渠道就是为了让用户不至于接受或者都不接受某个 app 的通知,而知你关心或者不关心的一些通知
// 1. build 构造函数都要传渠道号
 var build = NotificationCompat.Builder(this, "7602")

// 2. 在 application 中创建渠道,这个渠道是加入到系统设置中去的
// 所以无法修改,只有 add 不能 updata,只有在 app 删除时系统才会删除通知渠道
var notificationManager : NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val channel = NotificationChannel("7602", "test 渠道",NotificationManager.IMPORTANCE_HIGH)
    // 可以添加多个渠道进去
    notificationManager.createNotificationChannel(channel)
  }

Notification 发送步奏:

// 1. 拿到远程系统服务
var notificationManager : NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

// 2. 在 8.0 上添加渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val channel = NotificationChannel("7602", "test 渠道",NotificationManager.IMPORTANCE_HIGH)
    notificationManager.createNotificationChannel(channel)
  }

// 3. build 构建参数
var build = NotificationCompat.Builder(this, "7602")
    .setContentTitle("Title...")
    .setContentText("text...")
    .setSmallIcon(R.mipmap.ic_launcher)

// 4. 发送通知
notificationManager.notify(1, build.build())

build 中可配置参数

  • setContentTitle(CharSequence) - 设置标题
  • setContentText(CharSequence) - 设置内容
  • setSubText(CharSequence) - 设置内容下面一小行的文字
  • setTicker(CharSequence) - 设置收到通知时在顶部显示的文字信息
  • setSmallIcon(int) - 设置右下角的小图标,在接收到通知的时候顶部也会显示这个小图标
  • setLargeIcon(Bitmap) - 设置左边的大图标
  • setWhen(long) - 设置通知时间,一般设置的是收到通知时的System.currentTimeMillis()
  • setAutoCancel(boolean) - 用户点击Notification点击面板后是否让通知取消(默认不取消)
  • setDefaults(int) - 通知添加声音、闪灯和振动效果
    • Notification.DEFAULT_VIBRATE - 添加默认震动提醒
    • Notification.DEFAULT_SOUND - 添加默认声音提醒
    • Notification.DEFAULT_LIGHTS - 添加默认三色灯提醒
    • Notification.DEFAULT_ALL - 添加默认以上3种全部提醒
  • setVibrate(long[]) - 设置振动方式,比如:new long[] {0,300,500,700},延迟 0ms,振动300ms,在延迟 500ms, 接着再振动 700ms
  • setLights(int argb, int onMs, int offMs) - 设置三色灯,参数依次是:灯光颜色, 亮持续时间,暗的时间,不是所有颜色都可以,这跟设备有关,有些手机还不带三色灯; 另外,还需要为Notification设置flags为Notification.FLAG_SHOW_LIGHTS才支持三色灯提醒
  • setSound(Uri) - 设置接收到通知时的铃声
    • setDefaults(Notification.DEFAULT_SOUND) - 默认铃声
    • setSound(Uri.parse("file:///sdcard/xx/xx.mp3")) - 自定义铃声
    • setSound(Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "5")) - Android多媒体库内的铃声
  • setOngoing(boolean) - 设置为 ture,表示它为一个正在进行的通知,他们通常是用来表示一个后台任务,如播放音乐,文件下载
  • setProgress(int,int,boolean) - 带进度条的通知,参数依次为:进度条最大数值,当前进度,setProgress(0, 0, false) 可以移除进度条,setProgress(0, 0, true) 可以一直显示进度条
  • setContentIntent(PendingIntent) - 通知的点击事件
  • setVisibility(int) - 锁屏时显示不显示
    • Notification.VISIBILITY_PUBLIC- 显示所有通知内容
    • Notification.VISIBILITY_PRIVATE - 只显示标题
    • Notification.VISIBILITY_SECRET - 不显示任何内容

Notification 参数的说明

  1. Ticker 和 SubText 在哪显示


    SubText 挨着标题显示
Ticker 在通知来的那一瞬间显示
  1. setProgress

这里的 Progress 样式随着系统不同版本和手机厂商不同有所区别,样式不移动都一样,所以想要样式有没统一还是得采用自定义 Notification 的方式

SmallIcon 是必须设置的,不设置报错,SmallIcon 在左,LargeIcon 在右


注意点

  1. setSmallIcon
    setSmallIcon 必须设置的,要不然会直接报错

  2. 修改NotificationChannel
    NotificationChannel 之能往系统中添加,修改不存在的,相同 id 的 NotificationChannel 修改不生效,只有卸载 app 才会清除 NotificationChannel,替代方案是使用新 id 的 NotificationChannel


富文本

1. BigTextStyle

一般要是文字多了的话,一行显示不下时就是下面这样



这是我们可是使用 BigTextStyle 包裹文字


        var build = NotificationCompat.Builder(this, "7602")
                .setStyle( NotificationCompat.BigTextStyle().bigText("AAAAA") )
2. BigPictureStyle

带大图的样式


var bigPictureStyle = NotificationCompat.BigPictureStyle()
bigPictureStyle.bigPicture(BitmapFactory.decodeResource(resources, R.drawable.ic_dog))
bigPictureStyle.setSummaryText( "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" )

var build = NotificationCompat.Builder(this, "7602")
    .setStyle(bigPictureStyle)
3. InboxStyle

列表


val inboxStyle = NotificationCompat.InboxStyle()

inboxStyle.addLine("AAA")
inboxStyle.addLine("BBB")
inboxStyle.addLine("CCC")

build.setStyle(inboxStyle)
4. MediaStyle

其实还有这个的,但是我看大家都写的,没找到具体的资料


通知的取消和更新

  1. 取消通知
    取消通知我想大家都踩的到,就是用 id 来取消呗,既然在发送时用了 id ,那么必然大概率是可以通过 id 来取消的
  1. 更新通知
    利用上次的 build ,使用相同的 id 直接 notify 就行了,相同 id 的通知只能存在一个

优先级

使用 setPriority 方法可以设置通知的优先级,有五个级别:

  • PRIORITY_DEFAULT - 默认
  • PRIORITY_MIN - 最低,系统只会在用户下拉状态栏的时候才会显示
  • PRIORITY_LOW - 较低,系统会将这类通知缩小,或者改变显示的顺序,将排在更重要的通知之后
  • PRIORITY_HIGH - 较高,系统可能会将这类通知方法,或改变显示顺序,比较靠前
  • PRIORITY_MAX - 最重要的程度, 会弹出一个单独消息框,让用户做出相应

8.0 添加去到之后,不光 build 要设置,channel 一样在构造函数时要传优先级进去

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel("7602", "test 渠道", NotificationManager.IMPORTANCE_HIGH)
            notificationManager.createNotificationChannel(channel)
        }

        var build = NotificationCompat.Builder(this, "7602")
                .setPriority(NotificationManager.IMPORTANCE_HIGH)

PRIORITY_MAX 现在不好使了,channel 取值范围不支持这个参数了,最高到 PRIORITY_HIGH,另外手机厂商基本都懂通知这块了,随意这个优先级其实不怎惯用了,比如 8.0 之后你把 app 的通知标记为不重要了,这里你即便设置成 PRIORITY_HIGH 也不管用


响应通知点击

状态栏上的通知最最终是要用户来点击的,我们可以把 Notification 当做一个 Button 来处理,Button 可以设置 OnClickListener,Notification 因为是运行在系统进程上的,OnClickListener 对象传递过去也没有意思,这里借助的 intent,是把点击行为包装在 intent 中,没法做太多的花样,这个特殊的 intent 就是 PendingIntent

PendingIntent 是一种特殊的 Intent, 作用和 Intent 一样是用于启动系统组件,可以是:

当点击通知后,系统便会调用 PendingIntent,启动一个活动,服务或广播,这取决于你获取的是那种 PendingIntent。在 PendingIntent 中传入的 Context 销毁以后,PendingIntent 依旧有效,它一般使用在当 Context 销毁后需要执行 Intent的地方,一般不是用于立即执行的时候,比如在点击通知后唤醒一个 Activity。比如若是我们想要用 PendingIntent 大来一个页面,我们在后台 kill 该 app 进程,点击通知时依然会开发目标页面,和启动新的进程一样,该走的初始化都会走,比如 application onCreate 函数

// 启动 Activity 的 PendingIntent
var intent = Intent(this, NotificationReceiver::class.java)
var pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
var build = NotificationCompat.Builder(this, "7602")
    .setContentIntent(pendingIntent)

// 发送 Broadcast 的 PendingIntent
var intent2 = Intent("AAA")
var pendingIntent2 = PendingIntent.getBroadcast(this, 0, intent2, 0)
build.setContentIntent(pendingIntent2)


// 启动服务 的 PendingIntent
PendingIntent.getService(Context context, int requestCode, Intent intent, int flags);
PendingIntent.getActivities(Context context, int reqeustCode, Intent[] intents, int flags);
PendingIntent.getForgroundService(Context, int reqeustCode, Intent intent, int flags)

注意自 8.0 禁止静态广播后,经测试静态广播的方式不好用了。广播,服务,页面的 intent 里可以传具体指令或是参数,已实现通知和其他组件的通信


可以添加最多3个简单的按钮

如图就是这3个按钮
var actionBuilder1 = NotificationCompat.Action.Builder(R.drawable.ic_launcher_foreground, "Action1", pendingIntent)
var actionBuilder2 = NotificationCompat.Action.Builder(R.drawable.ic_launcher_foreground, "Action2", pendingIntent)
var actionBuilder3 = NotificationCompat.Action.Builder(R.drawable.ic_launcher_foreground, "Action3", pendingIntent)

build.addAction( actionBuilder1.build() )
build.addAction( actionBuilder2.build() )
build.addAction( actionBuilder3.build() )

自定义通知样式 RemoteViews

RemoteViews 是远程 view,我们通过系统 API 把 layoutID,复制操作发送到远程去显示


这个就是,使用自己的 layout xml 布局,但是有个坑,通知能支持的 RemoteViews 的高度是固定的,比如官方原话:

普通视图布局限制为 64 dp,扩展视图布局限制为 256 dp

但是国内的厂商啊基本都自己改改的,我手里这台魅族 16 th,估计能支持 80dp 高了,所以这里大家注意一下

另外 RemoteViews 能支持的布局类型和 view 不多,不支持的会直接报错,下面的是能支持的:

  • Layout
    • FrameLayout
    • LinearLayout
    • RelativeLayout
    • GridLayout
  • view
    • View Button
    • ImageView
    • ImageButton
    • TextView
    • ProgressBar
    • ListView
    • GridView
    • StackView
    • ViewStub
    • AdapterViewFlipper
    • ViewFlipper
    • AnalogClock
    • Chronometer

RemoteViews 不能 findViewById 来赋值,只能把 id 和值一起发给远程,包括点击也是这样的

RemoteViews remoteViews = new RemoteViews(getPackageName(),R.layout.notification_mobile_play);
remoteViews.setOnClickPendingIntent(R.id.btn_pre, getActivityPendingIntent(11));
remoteViews.setOnClickPendingIntent(R.id.btn_next, getActivityPendingIntent(12));
remoteViews.setOnClickPendingIntent(R.id.btn_start, getActivityPendingIntent(13));
remoteViews.setOnClickPendingIntent(R.id.ll_root, getActivityPendingIntent(14));
remoteViews.setTextViewText(R.id.tv_title, "标题");   
remoteViews.setTextViewText(R.id.tv_artist, "艺术家");  

自定义通知应用很多的,比如音视频操作,下载,app 辅助等,通知和 app 组件之间的交互都是通过服务或是广播来接收通知相关的点击事件


NotificationChannel 通知渠道

8.0 要大家添加这个通知渠道,然后渠道内也是可以设置一些参数的

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    //android 8.0以上需要特殊处理,也就是targetSDKVersion为26以上
    createNotificationChannel();
}

@TargetApi(Build.VERSION_CODES.O)
private void createNotificationChannel() {
    NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
    channel.canBypassDnd();//是否绕过请勿打扰模式
    channel.enableLights(true);//闪光灯
    channel.setLockscreenVisibility(VISIBILITY_SECRET);//锁屏显示通知
    channel.setLightColor(Color.RED);//闪关灯的灯光颜色
    channel.canShowBadge();//桌面launcher的消息角标
    channel.enableVibration(true);//是否允许震动
    channel.getAudioAttributes();//获取系统通知响铃声音的配置
    channel.getGroup();//获取通知取到组
    channel.setBypassDnd(true);//设置可绕过 请勿打扰模式
    channel.setVibrationPattern(new long[]{100, 100, 200});//设置震动模式
    channel.shouldShowLights();//是否会有灯光
    getManager().createNotificationChannel(channel);
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容