Android通知(Notification)使用详解

一、通知(Notification)

通知是可以在应用常规UI外部向用户显示的消息。通常使用NotificationCompat.Builder对象构建UI信息和操作,NotificationCompat.Builder.build()返回具体的Notification对象,最后通过调用NotificationManager.notify()将Notification对象传递给系统。

Notification必须包含以下内容:小图标(smallIcon),标题(ContentTitle),详细文本(ContentText)。

通知操作:当用户点击时触发的操作。一般会在应用打开Activity。在Notification内部,操作由PendingIntent定义,后者包含在应用中启动activity的Intent,通过setContentIntent()来添加PendingIntent。

通知优先级:优先级用于提醒设备UI如何显示通知。通过NotificationCompat.Builder,setPriority()并传入一个NotificationCompat优先级常量。共五个,从PRIORITY_MIN(-2)到PRIORITY_MAX(2);默认为PRIORITY_DEFAULT(0);

创建简单通知:

NotificationCompat.Builder mBuilder =
        new NotificationCompat.Builder(this)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!");
...
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// mId用于之后更新notification
mNotificationManager.notify(mId, mBuilder.build());
扩展布局:在4.1之前不可用。

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
    .setSmallIcon(R.drawable.notification_icon)
    .setContentTitle("多文本可扩展标题")
    .setContentText("多文本可扩展内容")
    // 多文本,可扩展
    .setStyle(new NotificationCompat.BigTextStyle()
            .bigText(msg))
    .addAction (R.drawable.ic_stat_dismiss,
            getString(R.string.dismiss), piDismiss)
    .addAction (R.drawable.ic_stat_snooze,
            getString(R.string.snooze), piSnooze);

...
兼容性处理:如,扩展通知仅在Android4.1及更高版本可用等。

为了确保兼容性,需要使用NorificationCompat及其子类创建通知。此外请遵循以下流程:

1.为用户提供通知的全部功能,无论何种版本的Android系统。因此要验证是否可以从应用的Activity中获得所有功能。要实现这个要求,可能要添加新的Activity;例如,若要使用addAction()提供停止和启动媒体播放的控件,应先在Activity中实现此控件。

2.确保用户均可通过点击通知启动Activity来获得该Activity中的功能。为此,要创建PendingIntent。调用setContentIntent()以将PendingIntent添加到通知。

3.将要使用的扩展通知功能添加到通知。添加的任何功能必须在用户点击通知时启动的activity中可用。

二、通知管理

1.更新通知

同一类型的事件多次发生时,要避免重新发送多次的通知。使用notify()所用的通知ID来更新或创建通知。

mNotificationManager =
        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// 设置通知id,以便更新
int notifyID = 1;
mNotifyBuilder = new NotificationCompat.Builder(this)
    .setContentTitle("New Message")
    .setContentText("You've received new messages.")
    .setSmallIcon(R.drawable.ic_notify_status)
numMessages = 0;
// 启动轮询处理数据,并通知用户
...
    mNotifyBuilder.setContentText(currentText)
        .setNumber(++numMessages);    // 设置该类型通知有多少个
    // 只要id一致,则通知就会更新而不是新建
    mNotificationManager.notify(
            notifyID,
            mNotifyBuilder.build());
...

2.删除通知

用户单独或通过使用“全部清除”清除了该通知(如果通知可以清除);

用户点击通知,并且创建通知时调用了setAutoCancel();

针对特定的通知ID调用了cancel()。此方法还会删除当前通知;

调用了cancelAll()方法,该方法删除之前所发出的所有通知。

三、启动Activity时保留导航

从通知启动Activity时,必须保留用户的预期导航体验。点击“返回”应使用户将应用的正常工作流返回到主屏幕,点击“最新动态”则应将Activity显示为单独的任务。要保留导航体验,应该在全新任务中启动Activity。如何设置PendingIntent以获得全新任务取决于启动的Activity的性质。一般有两种情况:

1.常规Activity

要启动的Activity是应用的正常工作流的一部分。这种情况下,设置PendingIntent以启动全新任务并为PendingIntent提供返回栈,这将重现应用的正常“返回”行为。例如正在别的应用时,点击了邮件的通知,点击返回,则会依次转到收件箱和主屏幕,而不是之前的那个应用。

1)在清单文件中定义应用的Activity层次结构;

a.添加对Android4.0.3及更低版本的支持。通过添加<meta-data>元素作为<activity>的子项来指定正在启动的Activity的父项(父Activity)。

b.添加对Android4.1及更高版本的支持,将android:parentActivityName属性添加到正在启动的Activity的<activity>元素中。

示例如下:

<activity
    android:name=".MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<activity
    android:name=".ResultActivity"
    android:parentActivityName=".MainActivity">
    <meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value=".MainActivity"/>
</activity>
2)根据可启动Activity的Intent创建返回栈:

a.创建Intent以启动Activity;

b.通过TaskStackBuilder.create()创建堆栈生成器;

c.通过调用addParentStack()将返回栈添加到堆栈生成器。对于在清单文件中所定义层次结构内的每个Activity,返回栈均可包含可启动Activity的Intent对象。此方法还会添加一些可在全新任务中启动堆栈的标志。(注:尽管addParentStack()的参数是对已启动Activity的引用,但是方法调用不会添加可启动Activity的Intent,而是留待下一步进行处理)

d.如果调用addNextIntent(),添加可从通知中启动Activity的Intent。将在第一步中创建的Intent作为addNextIntent的参数传递。

e.如需,通过调用TaskStackBuilder.editIntentAt()向堆栈中的Intent对象添加参数。有时,需要确保目标Activity在用户使用“返回”导航回它时会显示有意义的数据。

f.调用getPendingIntent()获得此返回栈的PendingIntent。使用此PendingItent作为setContentIntent()的参数。

...
Intent resultIntent = new Intent(this, ResultActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// 添加返回栈
stackBuilder.addParentStack(ResultActivity.class);
// 将intent添加到栈顶
stackBuilder.addNextIntent(resultIntent);
// 获取包含全部返回栈的PendingIntent
PendingIntent resultPendingIntent =
        stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
...
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(id, builder.build());

注意:此处的示例在模拟器上可能展现不出效果。

2.特殊Activity

仅当从通知启动时,用户才会看到此Activity。可以视Activity为通知的扩展。这种情况将PendingIntent设置为在全新的任务中启动。但由于启动的Activity不是应用Activity流程的一部分,因此无需创建返回栈。点击返回会将用户带到主屏幕。

不必在清单文件中定义Activity层次结构,也不必调用addParentStack()来构建返回栈。可以使用清单文件设置Activity任务选项,并通过调用getActivity()创建PendingIntent:

1)在清单中添加以下属性:

android:name="activityclass",全限定类名;

android:taskAffinity="",与在代码中设置的FLAG_ACTIVITY_NEW_TASK标志相结合,确保Activity不会进入应用的默认任务。任何具有应用默认关联的现有任务均不受影响。

android:excludeFromRecents="true",将新任务从“最新动态”中排除,这样用户就不会再无意中导航回它。

示例:

<activity
    android:name=".ResultActivity"
...
    android:launchMode="singleTask"
    android:taskAffinity=""
    android:excludeFromRecents="true">
</activity>
2)构建并发出通知

a.创建可启动Activity的Intent;

b.通过使用FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TASK标志调用setFlags(),将Activity设置为在新的空任务中启动;

c.为Intent设置所需的任何其他选项;

d.调用getActivity()从Intent中创建PendingIntent。使用该PendingIntent作为setContentIntent的参数。
示例:

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
// 为activity创建intent
Intent notifyIntent = new Intent(this, ResultActivity.class);
// 设置activity启动一个新的空栈
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                        | Intent.FLAG_ACTIVITY_CLEAR_TASK);
// 创建PendingIntent
PendingIntent notifyPendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// 把PendingIntent放到builder中
builder.setContentIntent(notifyPendingIntent);
// Notifications 发送给 NotificationManager
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// 通过builder构建一个匿名Notification对象并传递给NotificationManager
mNotificationManager.notify(id, builder.build());

四、在通知中显示进度

通知中可以显示进度条。如果可以估计操作时间以及完成进度,使用可指定进度的进度条。否则,使用无限循环进度条。

要在Android4.0及更高版本的平台上使用进度条,要调用setProgress()。早期版本,需要创建包含ProgressBar视图的自定义通知布局。

1.显示持续时间固定的进度指示器

通过setProgress(max, progress, false)将进度栏添加到通知,然后发出通知。操作结束,progress等于max。常见方式是,max为100,progress作为百分比值递增。

操作完成后可以保留进度栏,也可删除。但是都要更新通知文本以显示操作已完成。要删除进度栏,调用setProgress(0, 0, false)。

...
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this);
mBuilder.setContentTitle("Picture Download")
    .setContentText("Download in progress")
    .setSmallIcon(R.drawable.ic_notification);
// 在后台线程进行冗长操作
new Thread(
    new Runnable() {
        @Override
        public void run() {
            int incr;
            // 20次冗长操作
            for (incr = 0; incr <= 100; incr+=5) {
                    // 设置进度指示器最大值,完成值,确定状态
                    mBuilder.setProgress(100, incr, false);
                    // 第一次时,显示进度栏
                    mNotifyManager.notify(0, mBuilder.build());
                        try {
                            Thread.sleep(5*1000);
                        } catch (InterruptedException e) {
                            Log.d(TAG, "sleep failure");
                        }
            }
            // 当循环完成,更新notification
            mBuilder.setContentText("Download complete")
            // 移除进度栏
                    .setProgress(0,0,false);
            mNotifyManager.notify(ID, mBuilder.build());
        }
    }
// 启动线程
).start();

2.显示持续Activity指示器

使用setProgress(0, 0, true)添加到通知(忽略前两个参数),然后发出通知。setProgress(0, 0, false)可以删除指示器,不删除的话会一直显示。

五、自定义通知布局

使用RemoteViews对象自定义通知布局。自定义布局的可用高度取决于通知视图。普通视图布局限制为64dp,扩展视图布局限制为256dp。

首先实例化RemoteViews来扩充XML布局文件,调用setContent()方法设置详细内容,注意要使用RemoteViews中的方法设置视图子项的值:

1.在单独文件中为通知创建XML布局。

2.避免为RemoteViews设置背景Drawable,背景色可能使文本难以阅读。

这里按照文档描述的很简洁,以后出一个RemoteViews的详细版本。

六、其他功能

1.通知元数据(NotificationCompat.Builder)

1)当设备处于“优先”模式时,setCategory()会告知系统如何处理应用通知(如,通知代表电话呼入、消息、闹铃);

2)如果优先级设为PRIORITY_MAX或PRIORITY_HIGH,通知会有声音或振动,则setPriority()会将其显示在小型浮动窗口中;

3)addPerson()允许向通知添加人员名单。

2.浮动通知

5.0之后,设备处于活动状态时,即屏幕已打开,可以显示为浮动通知(在屏幕顶部出现notification,不需要拉开就能看见)。也可以提供一些操作按钮。

触发条件包括:用户的Activity处于全屏模式中(应使用fullScreenIntent),或者通知具有较高的优先级并使用铃声或振动。

fullScreenIntent要使用bulder.setFullScreenIntent(pendingIntent, true);// 官网上说有些系统会选择直接启动该Activity,有些则会显示为浮动通知。测三个真机都直接启动。

3.锁定屏幕通知

5.0之后,通知可以显示在锁定屏幕上。可以用此功能提示媒体播放控件。用户在“设置”中可选择通知是否显示在锁定屏幕上。

1)调用setVisibility()指定是否可见:

a.VISIBILITY_PUBLIC:显示通知的完整内容;

b.VISIBILITY_SECRET:不在锁定屏幕上显示;

c.VISIBILITY_PRIVATE:显示通知图标和内容等基本信息,隐藏内容。

这里是指在锁屏状态可以看见通知,但是不好说,国内定制机太多,像华为荣耀就不显示,魅族会显示,但是上面几个可见性不起作用,魅族全部显示。

2)锁屏状态控制媒体播放

Android5.0(API21),锁屏不再基于RemoteControlClient显示媒体控件。而是用Notification.MediaStyle与addAction()方法结合。后者将操作转换为可点击的图标。

注意:该模板和addAction()方法未包含在支持库中,因此只有5.0及以上版本才能支持。且要将可见性设置为VISIBILITY_PUBLIC。

下面给出代码,测试的时候魅族可以显示,华为荣耀7可以在下拉的通知栏里显示,但是不显示在锁屏屏幕上。

Notification notification = new Notification.Builder(context)
    // 设置可见性
    .setVisibility(Notification.VISIBILITY_PUBLIC)
    .setSmallIcon(R.drawable.ic_stat_player)
    // 添加媒体控制按钮调用媒体service中的intent。
    .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) // #0
    .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent)  // #1
    .addAction(R.drawable.ic_next, "Next", nextPendingIntent)     // #2
    // 应用媒体style模板
    .setStyle(new Notification.MediaStyle()
    .setShowActionsInCompactView(1 /* #1: pause button */)
    .setMediaSession(mMediaSession.getSessionToken())
    .setContentTitle("Wonderful music")
    .setContentText("My Awesome Band")
    .setLargeIcon(albumArtBitmap)
    .build();


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值