Notification的使用

Author Avatar
罗炜光 9月 08, 2016
  • 在其它设备中阅读本文章

一般步骤

使用状态栏通知一般有4个步骤:

1、 通过getSystemService()方法获取NotificationManager服务。

2、 创建一个Notification.Builder对象,并为其设置各种属性。

3、 对Notification.Builder对象设置各种属性和事件信息,构造一个对Notification对象。

4、 通过NotificationManager类的notify()方法将通知发送到状态栏。

NotificationManager

NotificationManager : 是状态栏通知的管理类,负责发通知、清除通知等。
NotificationManager 是一个系统Service,必须通过 getSystemService()方法来获取。

NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
  • public void cancel (int id)
    取消通知
  • public void cancel (String tag, int id)
    取消通知
  • public void cancelAll ()
    取消所有通知
  • public void notify (int id, Notification notification)
    发布通知
  • public void notify (String tag, int id, Notification notification)
    发布通知

Builder对象的方法

  • getNotification()
    API小于16时使用此方法构造一个Notification对象

  • build ()
    API大于等于16时使用此方法构造一个Notification对象

  • addAction (int icon, CharSequence title, PendingIntent intent)(在API23过时)
    添加按钮

  • addAction (Notification.Action action)(API20)
    添加按钮

  • setAutoCancel(boolean autoCancel)
    设置点击通知后,状态栏自动删除通知

  • setCategory(String category)(API21)
    设置通知类别

  • setColor(int argb)(API21)
    设置颜色

  • setContent(RemoteViews views)
    设置自定义视图

  • setContentInfo(CharSequence info)
    设置信息(即右下方显示的内容)

  • setContentIntent(PendingIntent intent)
    设置点击后意图,即打开哪个组件

  • setContentText(CharSequence text)
    设置内容

  • setContentTitle(CharSequence title)
    设置标题

  • setDefaults(int defaults)
    向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合,如:setDefaults(Notification.DEFAULT_SOUND|Notification.DEFAULT_VIBRATE)

    • Notification.DEFAULT_VIBRATE //添加默认震动提醒 需要 VIBRATE permission
    • Notification.DEFAULT_SOUND // 添加默认声音提醒
    • Notification.DEFAULT_LIGHTS// 添加默认三色灯提醒
    • Notification.DEFAULT_ALL// 添加默认以上3种全部提醒
  • setDeleteIntent(PendingIntent intent)
    设置删除时的意图

  • setExtras(Bundle extras)(API19)
    设置数据

  • setFullScreenIntent(PendingIntent intent, boolean highPriority)
    设置悬挂式Notification

  • setGroup(String groupKey)(API20)
    设置该通知组的密匙,即确认为哪一组

  • setGroupSummary(boolean isGroupSummary)(API20)
    设置是否为一组通知的汇总

  • setLargeIcon(Icon icon)(API23)
    设置大图标

  • setLargeIcon(Bitmap b)
    设置大图标

  • setLights(int argb, int onMs, int offMs)
    设置三色灯,ledARGB 表示灯光颜色、 ledOnMS 亮持续时间、ledOffMS 暗的时间

  • setLocalOnly(boolean localOnly)(API20)
    设置该通知是否应不桥接至其它设备。

  • setNumber(int number)
    设置一个数(即右下方显示的内容)

  • setOngoing(boolean ongoing)
    设置为true时就不能滑动删除

  • setOnlyAlertOnce(boolean onlyAlertOnce)
    设置仅提醒一次

  • setPriority(int pri)(API16)
    设置优先级

优先级 用户
MAX 重要而紧急的通知,通知用户这个事件是时间上紧迫的或者需要立即处理的。
HIGH 高优先级用于重要的通信内容,例如短消息或者聊天,这些都是对用户来说比较有兴趣的。
DEFAULT 默认优先级用于没有特殊优先级分类的通知。
LOW 低优先级可以通知用户但又不是很紧急的事件。
MIN 用于后台消息(例如天气或者位置信息)。最低优先级通知将只在状态栏显示图标,只有用户下拉通知抽屉才能看到内容。
  • setProgress(int max, int progress, boolean indeterminate)(API14)
    设置进度条

  • setPublicVersion(Notification n)
    设置安全锁屏下的通知

  • setShowWhen(boolean show)(API17)
    是否显示时间

  • setSmallIcon(int icon, int level)
    设置小图标

  • setSmallIcon(int icon)
    设置小图标.

  • setSmallIcon(Icon icon)
    设置小图标

  • setSortKey(String sortKey)(API20)
    设置排序键

  • setSound(Uri sound, AudioAttributes audioAttributes)(API21)
    设置铃声

  • setSound(Uri sound)
    设置铃声

  • setSound(Uri sound, int streamType)(在API21废弃)
    设置铃声

  • setStyle(Notification.Style style)(API16)
    设置样式

  • setSubText(CharSequence text)(API16)
    设置第三行的文本

  • setTicker(CharSequence tickerText, RemoteViews views)
    设置显示在状态栏的提醒内容(5.0及之后没有效果)

  • setTicker(CharSequence tickerText)
    设置显示在状态栏的提醒内容(5.0及之后没有效果)

  • setUsesChronometer(boolean b)(API16)
    使用计时器

  • setVibrate(long[] pattern)
    设置震动,其中数组的奇数位为暂停时间,偶数位为震动时间

  • setVisibility(int visibility)(API21)
    设置可达性

    • VISIBILITY_PUBLIC 只有在没有锁屏时会显示通知
    • VISIBILITY_PRIVATE 任何情况都会显示通知
    • VISIBILITY_SECRET 在安全锁和没有锁屏的情况下显示通知
  • setWhen(long when)
    设置显示通知的时间,不设置默认获取系统时间,这个值会在Notification上面显示出来

如果不设置LargeIcon,那么系统会默认将上面的SmallIcon显示在通知选项的最左侧,右下角的小图标将不再显示

notification.flags参数介绍

Notification.FLAG_SHOW_LIGHTS //三色灯提醒,在使用三色灯提醒时候必须加该标志符

Notification.FLAG_ONGOING_EVENT //发起正在运行事件(活动中)

Notification.FLAG_INSISTENT //让声音、振动无限循环,直到用户响应 (取消或者打开)

Notification.FLAG_ONLY_ALERT_ONCE //发起Notification后,铃声和震动均只执行一次

Notification.FLAG_AUTO_CANCEL //用户单击通知后自动消失

Notification.FLAG_NO_CLEAR //只有全部清除时,Notification才会清除 ,不清楚该通知(QQ的通知无法清除,就是用的这个)

Notification.FLAG_FOREGROUND_SERVICE //表示正在运行的服务

PendingIntent

PendingIntent是一个Intent的描述、包装,给予了这个PendingIntent 的组件在指定的事件发生或指定的时间到达时启动Activty、Service或者Broadcast。

PendingIntent contentIntent = PendingIntent.getActivity(context,
requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent contentIntent = PendingIntent.getBroadcast(context,
requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent contentIntent = PendingIntent.getService(context,
requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
  • FLAG_ONE_SHOT
    当前描述的PendingIntent只能被使用一次,然后它就会被自动cancel,如果后续还有相同的PendingIntent,那么他们的send方法就会调用失败。对于通知栏消息来说,如果采用此标记,那么同类的通知只能使用一次,后续的通知单击后将无法打开

  • FLAG_NO_CREATE
    当前描述的PendingIntent不会主动创建,如果当前PendingIntent之前不存在,那么getActivity、getService、getBroadcast方法会直接返回null,即获取PendingIntent失败。这个标记很少见,它无法单独使用,因此在日常开发中它并没有太多的使用意义

  • FLAG_CANCEL_CURRENT
    如果描述的PendingIntent已经存在,那么它们都会被cancel,然后系统会创建一个新的PendingIntent。对于通知栏消息来说,那些cancel的消息单击后将无法打开。

  • FLAG_UPDATE_CURRENT
    当前描述的PendingIntent如果已经存在,那么它们都会被更新,即它们的Intent中的Extras会被替换成最新的

上面4个flag中最经常使用的是FLAG_UPDATE_CURRENT,因为描述的Intent有 更新的时候需要用到这个flag去更新你的描述,否则组件在下次事件发生或时间到达的时候extras永远是第一次Intent的extras。
使用 FLAG_CANCEL_CURRENT也能做到更新extras,只不过是先把前面的extras清除,另外FLAG_CANCEL_CURRENT和 FLAG_UPDATE_CURRENT的区别在于能否新new一个Intent,FLAG_UPDATE_CURRENT能够新new一个 Intent,而FLAG_CANCEL_CURRENT则不能,只能使用第一次的Intent。

此外还需要注意参数: int requestCode

对于FLAG_UPDATE_CURRENT,如果上面的requestCode 为常量,则对于先后出现的若干Notification,则所有对应的Intent里面的extra被更新为最新的,就是全部同一为最后一次的。
相反,如果requestCode每次不一样,则里面的Inent的数据没被更新。
对于FLAG_CANCEL_CURRENT,则只响应最前面的第一条Notifiacation,后面所有的不响应….

自定义Notification

实现自定义Notification的步骤

  • 创建RemoteViews对象
  • Notification.Builder对象中使用setContent(RemoteViews views)方法

RemoteViews

RemoteViews表示的是一个view结构,它可以在其他进程中显示。由于它在其他进程中显示,为了能够更新它的界面,RemoteViews提供了一组基础的操作用于跨进程更新它的界面

支持类型

在RemoteViews并不支持所有的View类型,支持的所有类型如下:
Layout:
FrameLayout, LinearLayout, RelativeLayout,GridLayout

View:
AnalogClock, Button, Chronometer, ImageButton, ImageView, ProgressBar, TextView, ViewFlipper, ListView, GridView, StackView, AdapterViewFlipper,ViewStub

上面说描述的RemoteViews所支持的所有的View类型,RemoteViews不支持它们的子类以及其他View类型。也就是说RemoteViews中不能使用除了上述列表中以外的View,也无法使用自定义View

常用方法

  • setTextViewText(int viewId,Charsequence text)
    设置TextView的文本

  • setTextViewTextSize(int viewId, int units, float size)
    设置TextView的字体大小

  • setTextColor(int viewId, int color)
    设置TextView的字体颜色

  • setImageViewResource(int viewId, int srcId)
    设置ImageView的图片资源

  • setInt(int viewId, String methodName, int value)
    反射调用View对象的参数类型为int的方法

  • setLong(int viewId, String methodName, long value)
    反射调用View对象的参数类型为long的方法

  • setBoolean(int viewId, String methodName, boolean value)
    反射调用View对象的参数类型为boolean的方法

  • setOnClickPendingIntent(int viewId, PendingIntent pendingIntent)
    为View添加单击事件,事件类型只能为PendingIntent

内部机制

  • RemoteViews的构造方法 public RemoteViews(String packageName, int layoutId),第一个参数是当前应用的包名,第二个参数是待加载的布局文件。

  • RemoteViews提供了一系列的set方法完成view的设置,这是通过反射完成的调用的。例如方法setInt(int viewId, String methodName, int value)就是反射调用view对象的名称为methodName的方法,传入参数value,同样的还有setBoolean、setLong等。方法setOnClickPendingIntent(int viewId, PendingIntent pi)用来为view添加单击事件,事件类型只能为PendingIntent。

  • 系统对View界面执行一系列的更新操作,即通过set方法提交的,但更新操作不是立即执行(在RemoteViews内部会记录所有的更新操作),而是到RemoteViews被加载后执行

  • 通知和小部件分别由NotificationManager和AppWidgetManager管理,而它们通过Binder分别和SystemServer进程中的NotificationManagerService和AppWidgetManagerService进行通信。所以,布局文件实际上是两个Service加载的,运行在SystemServer进程中。

  • RemoteViews实现了Parcelable接口,它会通过Binder传递到SystemServer进程,系统会根据RemoteViews中的包名信息获取到应用中的资源,从而完成布局文件的加载。

  • 系统将view操作封装成Action对象,Action同样实现了Parcelable接口,通过Binder传递到SystemServer进程。远程进程通过RemoteViews的apply方法来进行view的更新操作,RemoteViews的apply方法内部则会去遍历所有的action对象并调用它们的apply方法来进行view的更新操作。这样做的好处是不需要定义大量的Binder接口,其次批量执行RemoteViews中的更新操作提高了程序性能。

  • RemoteViews的apply和reapply方法的区别:apply方法会加载布局并更新界面,而reapply方法则只会更新界面。

  • setOnClickPendingIntent、setPendingIntentTemplate和setOnClickFillIntent的区别,setOnClickPendingIntent用于给普通的view添加点击事件,但是不能给集合(ListView和StackView)中的view设置点击事件,因为开销太大了。如果需要给ListView和StackView中的item添加点击事件,需要结合setPendingIntentTemplate和setOnClickFillIntent一起使用。

假如报了android.app.RemoteServiceException: Bad notification posted from package ****: Couldn’t expand RemoteViews for:****问题,那么可能不一定是写错了,而是使用了Install Run功能,关闭此功能或每次修改自定义Notification的xml布局文件都要在运行前删除应用 *

TaskStackBuilder(API16)

当我们想要跳转的Activity按返回时返回的是应用的主界面或其他界面,而不是桌面时,可以使用此类来构建一个任务栈

步骤

  1. 创建TaskStackBuilder对象
  2. 通过addNextIntent (Intent nextIntent)添加跳转的intent
  3. 重复第二步,直到完成任务栈的创建
  4. 通过getPendingIntent()方法获得PendingIntent
  5. 设置跳转的PendingIntent

创建TaskStackBuilder对象

TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(this);

当Activity设置了android:parentActivityName属性时,使用addParentStack()方法可以直接创建其指定的所有上级进栈(但不包括自己),使用addNextIntentWithParentStack()包括自己及所有上级进栈

例子

TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(this);
Intent intent = new Intent(this,MainActivity.class);
taskStackBuilder.addNextIntent(intent);
Intent intent2 = new Intent(this,SecondActivity.class);
taskStackBuilder.addNextIntent(intent2);
Intent intent3 = new Intent(this,ThirdActivity.class);
taskStackBuilder.addNextIntent(intent3);
int requestCode = (int) SystemClock.uptimeMillis();
remoteViews.setOnClickPendingIntent(R.id.test,
                                    taskStackBuilder.getPendingIntent(requestCode,
                                    PendingIntent.FLAG_UPDATE_CURRENT));

常见Notification类型

普通Notification

点击取消显示的Notification

  • builder中调用setAutoCancel(true)(Notification需要设置ContentIntent才有效)

无法滑动删除的Notification

  • builder中调用setOngoing(true)

拥有按钮的Notification

  • builder中使用addAction()添加按钮

折叠式Notification

//设置展开后的视图
notification.bigContentView = remoteViews;

横幅式Notification

  • builder对象调用setFullScreenIntent()方法

进度条Notification

  • builder对象调用setProgress()方法

BigTextStyle样式的Notification

  • 创建Notification.BigTextStyle对象
  • 使用setBigContentTitle()设置标题
  • 使用bigText()设置正文
  • 使用setSummaryText()设置末尾行文本
  • 使用setStyle()设置此style对象

BigPictureStyle样式的Notification

  • 创建Notification.BigPictureStyle对象
  • 使用setBigContentTitle()设置标题
  • 使用setSummaryText()设置文本
  • 使用bigPicture()设置大图,但是太大会造成OOM
  • 使用bigLargeIcon()设置图标
  • 使用setStyle()设置此style对象

InboxStyle样式的Notification

  • 创建Notification.InboxStyle对象
  • 使用setBigContentTitle()设置标题
  • 使用addLine()添加行文本,调用n次则生成n行文本
  • 使用setSummaryText()设置末尾行文本
  • 使用setStyle()设置此style对象

MediaStyle样式的Notification

  • builder中使用addAction()添加按钮
  • 使用setMediaSession()设置MediaSession.Token对象
  • 使用setShowActionsInCompactView()方法设置显示在通知右方的图标 最多三个
  • 使用setStyle()设置此style对象

参考资料

Android开发艺术探索
【Android】状态栏通知Notification、NotificationManager详解
Notification中PendingIntent.Flag的应用
Android 通知栏Notification的整合 全面学习 (一个DEMO让你完全了解它)
Android Notification常见样式总结