Android通知Notification详解

Notification简介

通知是在常规UI界面之外向用户展示消息的工具,当系统发出通知时,它会以图表的形式显示在状态栏中。此时打开通知栏,就可以看到通知的详细信息了。创建通知由Notification.Builder类来控制(API 11添加)。另外,v4的支持库也提供了创建通知的类 NotificationCompat.Builder。在API 11 之前,通知的创建由new Notification()直接创建,现在不建议使用了。

创建Notification

创建通知通过NotificationCompat.Builder,创建完成之后,调用NotificationCompat.Builder.build()方法获取Notification对象,此时通知已经创建好了,下一步发出通知使用NotificationManager.notify()就可以将通知发送出去了。

创建通知的时候可以在Builder中对通知进行设置,包括图标,标题,点击响应等等。有三个基本参数是必须要包含的:

小图标,由 setSmallIcon() 设置
标题,由 setContentTitle() 设置
详细文本,由 setContentText() 设置

下面是一个通知的例子,基本上包含了常用的各项设置:

Notification notification = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.ic_action_new)    //设置小图标,用于状态栏左上角显示
                .setContentTitle("Notification Title")      //设置标题
                .setContentText("Notification content text.")
                .setContentInfo("Content info")
                .setSubText("sub text")
                .setTicker("ticker...")                      //通知到来时状态栏闪过的信息,API21以后不再显示
                .setDefaults(NotificationCompat.DEFAULT_ALL)//设置默认的灯光,提示音和振动,可选值:DEFAULT_ALL,DEFAULT_SOUND,DEFAULT_VIBRATE,DEFAULT_LIGHTS
//                .setLights()
//                .setSound()
//                .setVibrate()
                .setNumber(10)                             //已经被 setSubText取代,显示在时间下方的内容
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)//5.0以后添加
                .setWhen(System.currentTimeMillis())        //设置通知上显示的时间
                .setShowWhen(true)                          //通知是否显示时间
                .setUsesChronometer                         // 设置是否显示时钟表示时间
                .setChronometerCountDown(false);   // 设置时钟是否为倒计时
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.largeicon))  //设置大图标
                .setPriority(NotificationCompat.PRIORITY_MAX) //设置等级,可选值有PRIORITY_MIN(-2),PRIORITY_LOW(-1),PRIORITY_DEFAULT(0),PRIORITY_HIGH(1),PRIORITY_MAX(2),默认为0
                .setOngoing(false)                            //设置是否为正在进行的通知。设置为true表示有相关动作正在执行,比如播放音乐。ongoing状态的通知是不会自动消失,也不能手动清除的。除非调用Manager的cancel方法
                .setAutoCancel(true)                          //是否自动消失,true表示响应点击之后自动消失。
                .build();                                     //API16添加,API11使用getNotification()

        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        manager.notify(1, notification);

setVisibility方法有以下可选值:

  • VISIBILITY_PUBLIC 任何情况下都显示通知的完整内容。
  • VISIBILITY_SECRET 不会在锁定屏幕上显示此通知的任何部分。
  • VISIBILITY_PRIVATE 显示通知图标和内容标题等基本信息,但是隐藏通知的完整内容。

设置 VISIBILITY_PRIVATE 后,还可以通过 setPublicVersion() 提供其中隐藏了某些详细信息的替换版本通知内容。

PendingIntent

为通知设定点击事件,需要使用PendingIntent实现。与Intent相比,PendingIntent表示一种特定的处于等待状态,即将发生的一种状态,而Intent则是马上发生的。PendingIntent可以表示三种意图:

  • getActivity(Context context, int requestCode, Intent intent, int flags)
  • getService(Context context, int requestCode, Intent intent, int flags)
  • getBroadcast(Context context, int requestCode, Intent intent, int flags)

requestCode 表示PendingIntent发送方的请求码,它会对PendingIntent的匹配产生影响,当PendingIntent包含的Intent相同,而且requestCode也相同,系统就为认为这是同样的PendingIntent(Extras不影响)。

flags常用的值有四个:

  • FLAG_ONE_SHOT 表示该PendingIntent只能被使用一次,使用完之后就自动cancel。
  • FLAG_NO_CREATE 不会主动创建PendingIntent,如果之前不存在该PendingIntent,get方法返回null,调用失败。
  • FLAG_CANCEL_CURRENT 当前PendingIntent如果已经存在了,那么cancel掉存在的,然后重新创建一个。
  • FLAG_UPDATE_CURRENT 当前PendingIntent如果已经存在了,它们都会被更新,替换成新的Extras。

我们使用notify发送通知的时候,如果id相同,会被认为是同一个通知,更新通知的状态。如果发送一系列id不同,内容相同的通知(包括同样的PendingIntent),这时候点击事件就会根据不同的flag做出不同的判断了。

系统会认为这一系列通知的PendingIntent是同样的,所以如果flag是FLAG_ONE_SHOT,第一个点击的通知会响应事件,剩余的通知在点击的时候就不会有动作了,因为PendingIntent已经被cancel。如果flag是FLAG_CANCEL_CURRENT,那么,每次发送一个通知,系统就会发现PendingIntent已经存在了,于是cancel掉存在的PendingIntent,再生成一个新的,只能给自己用,所以只有最后一个会被响应。如果是FLAG_UPDATE_CURRENT,每发送一条,它都会更新当前存在的所有PendingIntent,并替换为最新的Extras,所以,每个通知都会正常响应。

为通知设置事件,通常情况下这两个参数都设置为0就可以了:

  Intent intent = new Intent(this, SplashActivity.class);
  PendingIntent pi = PendingIntent.getActivity(this, 0 , intent, 0);
  builder.setContentIntent(pi)

设置PendingIntent有三个方法:

  • setContentIntent 点击通知内容
  • setDeleteIntent() 通知被清除时
  • setFullScreenIntent() 全屏事件的Intent,比如在通话时。设置了该项,通知会以浮动的形式闪出

浮动通知是5.0新加的功能,如果通知的优先级设置较高,或者设置了setFullScreenIntent,通知到来的时候手机处于活动状态(点亮屏幕且未锁屏),这是通知会从顶部自动闪出,并且可以响应点击。

事件进阶-TaskStackBuilder

Android中使用任务栈来管理Activity,这个应该都很清楚了,现在考虑这么一种情况:通过点击通知栏打开一个Activity,这时候,通过Back返回,就会销毁当前Activity,直接返回桌面了,如果我们想返回该应用的首页,而不是退出,就要用到TaskStackBuilder。

我们先看一下TaskStackBuilder的使用方法:

        Intent intent = new Intent(this, SplashActivity.class);// 构建一个指向SplashActivity.class的Intent
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);//获取TaskStackBuilder 
        stackBuilder.addParentStack(SplashActivity.class)
        stackBuilder.addNextIntent(intent);
        PendingIntent pi2 = stackBuilder.getPendingIntent(0,0);

首先通过TaskStackBuilder.create(this)创建一个任务栈,下一步方法是addParentStack,这个方法比较奇葩,我们这里指定的是SplashActivity.class,实际上它添加的并不是SplashActivity。它需要与Manifest中的对应Activity的parentActivityName属性结合使用。

<activity
            android:name=".SplashActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:parentActivityName=".DrawableActivity"
            >

SplashActivity指定了它的父Activity为DrawableActivity,所以我们调用addParentStack(SplashActivity.class)之后,此时返回栈中只是添加了DrawableActivity

addNextIntent用来继续向返回栈添加Activity,这里才将SplashActivity添加进来。当然我们也可以继续创建指向其他Activity的Intent,使用addNextIntent向返回栈中添加新的Activity,最后添加的Activity处于最顶层,依次向下。(实际上addParentStack并不是必须的,系统使用addNextIntent添加Activity进栈时,仍然会读取它的android:parentActivityName并添加对应的父Activity。另外,请不要对被设为parentActivity的Activity再设置android:parentActivityName,会导致ANR)

使用TaskStackBuilder之后,不能根据intent来创建PendingIntent了,需要使用getPendingIntent方法,获取到之后设置给Notification,这样就实现了指定返回Activity的功能。

为Activity指定任务栈

上面介绍了创建任务栈并管理其中的Activity,从而实现返回指定Activity的功能。但是,有时候我们需要的功能很简单,点击通知打开Activity,按下Back键返回桌面。此时,如果Activity所在的应用处于开启状态,新开启的Activity会直接添加到当前应用的栈中,所以按下Back之后返回的是应用而不是桌面。

要实现这种功能很简单,为新建的Activity创建新的任务栈就可以了。

首先设置要启动的Activity属性:

<activity>
......
    android:taskAffinity=""  //指定任务栈的Affinity,与Intent的NEW_TASK相结合,在新的任务栈启动
    android:excludeFromRecents="true">//不显示在最近任务列表
</activity>

然后:
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 再设置PendingIntent给通知就可以了。FLAG_ACTIVITY_CLEAR_TASK表示清空要启动的Activity所处的任务栈,确保android:taskAffinity指定的值唯一的情况下可以不设置。

其他常用设置

  • 进度条通知
    使用setProgress(max, progress, false)来设置及进度条通知,并更新进度。第三个参数设置为true表示循环滚动不显示准确进度的进度条。注意进度条与subContent不能同时设置,否则进度条不能显示。

  • 设置样式setStyle()

    系统已经提供了几个样式给我们使用,有BigPictureStyleBigTextStyleMessagingStyleInboxStyle等。

自定义外观

我们可以通过RemoteView来对通知栏进行自定义View的设置。有四个相关方法

setContent 设置普通视图,高度限制为 64 dp
setCustomContentView设置普通视图,高度限制为 64 dp
setCustomBigContentView() 设置扩展视图,高度可以扩展到256dp
setCustomHeadsUpContentView() 设置浮动通知视图

推荐阅读更多精彩内容