android notification完全解析

Notifications in Android 4.4 and Lower

notification是很重要的部分,它与service,BroadcastReceiver,Intent等密切相关,很好地使用notification可以使你的app的活跃度相对于没有notification 的app大增。

很明显这篇文章将详细的介绍4.4及以下的notification。至于,android 5.0的相关特性,我计划在假期里开一个特辑来写。下载就当是回顾知识了。毕竟,温故而知新嘛,哈哈。

The notification system allows your app to keep the user informed about events,
 such as new chat messages or a calendar event. Think of notifications as a 
 news channel that alerts the user to important events as they happen or a log 
 that chronicles events while the user is not paying attention.

notification使得你的app可以告知用户app中的事件,例如一条新的message或者一个日历事件。可以认为notification是一个新闻渠道,它用来在用户不注意到的时候来通知用户一些重要事件。

Anatomy of a notification

Base Layout

At a minimum, all notifications consist of a base layout, including:

the sending application's notification icon or the sender's photo
a notification title and message
a timestamp
a secondary icon to identify the sending application when the sender's image is     shown for the main icon

notification的基本布局

最低限度,所有的notifications 都由基本布局构成,包括:

发送notification的application的icon或者发送者的image。
notification的title和message。
时间戳
当main icon用来显示sender’s image时 Secondary icon用来显示发送application的icon。

Expanded layouts

You have the option to provide more event detail. You can use this to show the 
first few lines of a message or show a larger image preview. This provides the 
user with additional context, and - in some cases - may allow the user to read 
a message in its entirety. The user can pinch-zoom or two-finger glide in order 
to toggle between base and expanded layouts. For single event notifications, 
Android provides two expanded layout templates (text and image) for you to re-
use in your application.

notification的扩展布局

你可以选择提供事件的更多细节。你可以用它来显示一个message的一些行或者显示一个图片的预览。这些给用户提供了额外的内容,并且,在一些情况下,可以允许用户阅读完整的信息。用户可以通过 pinch-zoom 或者双手指滑动来打开扩展布局。android为单个notification提供了两种扩展布局的方式(image,text)以供你在你的application中使用。

Alt text

Alt text

Alt text

1.notification的title
2.发送notification的application的icon或者发送者的image。
3.notification的message
4.notification的数目显示
5.当main icon用来显示sender’s image时 Secondary icon用来显示发送application的icon。
6.时间戳,默认为系统发出通知的时间。

Actions

Android supports optional actions that are displayed at the bottom of the 
notification. With actions, users can handle the most common tasks for a 
particular notification from within the notification shade without having to 
open the originating application. This speeds up interaction and, in 
conjunction with "swipe-to-dismiss", helps users to streamline their 
notification triaging experience.

Be judicious with how many actions you include with a notification. The more 
actions you include, the more cognitive complexity you create. Limit yourself 
to the fewest number of actions possible by only including the most imminently 
important and meaningful ones.

Good candidates for actions on notifications are actions that are:

essential, frequent and typical for the content type you're displaying
time-critical
not overlapping with neighboring actions
Avoid actions that are:

ambiguous,duplicative of the default action of the notification (such as "Read" or "Open")

android支持在notification的地步显示可选的操作。有了这些actions,用户可以处理大多数相同的tasks而无需打开应用。这集快乐交互的速度,并且结合滑动消失使得用户简化其通知处理的经历。

明智的决定在你的notification里面放置的action的数量。如果你包含了更多的action,那么你便增加了交互的复杂性。请选择一个或者两个很重要并且有意义的action。

好的候选action有以下的特点:

.对该通知时重要的,典型的,并且频繁使用的action。
.时间紧迫的
.不要和相邻的action产生重叠

避免以下操作:

.含糊不清地
.和点击notification的action重复的(such as “Read” or “Open”)

Design guidelines

.个性化
.导航到正确的地方
.正确的设置通知的优先级

主要看一下优先级的设置吧,其他的解释大家可以参考官网。
用方法setPriority(int pri)来指定优先级

Alt text

对应的属性如下:

Notification.PRIORITY_DEFAULT(优先级为0)
Notification.PRIORITY_HIGH
Notification.PRIORITY_LOW
Notification.PRIORITY_MAX(优先级为2)
Notification.PRIORITY_MIN(优先级为-2)

现在,我们来看一下,如何实现一个notification。估计大家现在最常用的做法是下面这种:

Notification notification = new Notification(R.drawable.ic_launcher, 
getText(R.string.app_name),
           System.currentTimeMillis());

   Intent notificationIntent = new Intent(this, MyService.class);
   PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, 
   notificationIntent, 0);
   notification.setLatestEventInfo(this, getText(R.string.hello_world),
           getText(R.string.hello_world), pendingIntent);
   startForeground(1, notification);

这是在service里面启动一个notification,但是大家一定记住这种方法已经过时了,也就是说Google官方已经不提倡使用这种方法来启动一个notification了。官方推荐使用V4包下NotificationCompat.Builder,这样,我们便可以设置各种属性。

好了,现在我们具体来看一下,Notification的具体实现。

1.创建一个简单的notification。

Alt text

Required notification contents
notification必须要包含的内容

A Notification object must contain the following:
一个notification必须包含下面的属性:

A small icon, set by setSmallIcon()
一个small icon,用setSmallIcon()来设置,对应于上图中的2号区域

A title, set by setContentTitle()
一个title,用setContentTitle()来设置,对应于上图中的1号区域

Detail text, set by setContentText()
详细文本,用setContentText()来设置,对应于上图中的3号区域

这三个是必须设置的,至于其他的扩展则是需求而定。代码如下:

 private NotificationManager manager;

NotificationCompat.Builder notifyBuilder;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    /*实例化NotificationManager以获取系统服务*/
    manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

}

/**
 * 显示最简单的通知,以下method中的三个set方法是必须设置的
 *
 * @param view
 */
public void simNotification(View view) {

    Toast.makeText(this, "hha", Toast.LENGTH_LONG).show();

    notifyBuilder = new NotificationCompat.Builder(this)
            /*设置small icon*/
            .setSmallIcon(R.drawable.ic_launcher)
            /*设置title*/
            .setContentTitle("通知")
            /*设置详细文本*/
            .setContentText("Hello world");
    manager.notify(100, notifyBuilder.build());
}

代码里的注释已经很详细了,simNotification绑定到了一个Button。这样,我们已经实现了最简单的notification。

2.创建一个点击跳转到其它Activity的Notification。

 /**
 * 点击跳转到指定Activity
 *
 * @param view
 */
public void largePicture(View view) {
     /*实例化NotificationManager以获取系统服务*/
    manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

    //点击的意图ACTION是跳转到Intent
    Intent resultIntent = new Intent(this, MainActivity.class);
    resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
    notifyBuilder = new NotificationCompat.Builder(this)
            /*设置large icon*/
            .setLargeIcon(bitmap)
             /*设置small icon*/
            .setSmallIcon(R.drawable.ic_launcher)
            /*设置title*/
            .setContentTitle("通知")
            /*设置详细文本*/
            .setContentText("Hello world")
             /*设置发出通知的时间为发出通知时的系统时间*/
            .setWhen(System.currentTimeMillis())
             /*设置发出通知时在status bar进行提醒*/
            .setTicker("来自问月的祝福")
            /*setOngoing(boolean)设为true,notification将无法通过左右滑动的方式清除
            * 可用于添加常驻通知,必须调用cancle方法来清除
            */
            .setOngoing(true)
             /*设置点击后通知消失*/
            .setAutoCancel(true)
             /*设置通知数量的显示类似于QQ那种,用于同志的合并*/
            .setNumber(2)
             /*点击跳转到MainActivity*/
            .setContentIntent(pendingIntent);

    manager.notify(121, notifyBuilder.build());
}

代码里的注释都很详细,加上前面的介绍相信大家都能理解了。

3.创建一个显示bitmap的notification,类似于屏幕截图的显示效果

Alt text

/**

 * 类似于系统截图的效果
 *
 * @param view
 */
public void comNotification(View view) {

    /*实例化NotificationManager以获取系统服务*/
    manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

    /*获取bitmap*/
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
    notifyBuilder = new NotificationCompat.Builder(this)
             /*设置small icon*/
            .setSmallIcon(R.drawable.ic_launcher)
            /*设置title*/
            .setContentTitle("通知")
            /*设置详细文本*/
            .setContentText("Hello world")
            .setWhen(System.currentTimeMillis())
            .setOngoing(true)
            .setNumber(2);

    NotificationCompat.BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle();
    bigPictureStyle.bigPicture(bitmap);
    notifyBuilder.setStyle(bigPictureStyle);

    manager.notify(121, notifyBuilder.build());
}

这里唯一需要注意的是加载图片最好不要在UI线程进行(这里只是为了演示)。

4.创建一个类似于日历事件的notification,并与Service进行交互。

Alt text

 /**
 * 创建一个类似于日历事件的notification
 * @param view
 */
public void add_action(View view) {

    myIntent = new Intent(getApplicationContext(), MyIntentService.class);

    myIntent.putExtra(myConstants.EXTRA_MESSAGE, " 来自问月的祝福");
    myIntent.setAction(myConstants.ACTION_PING);
    myIntent.putExtra(myConstants.EXTRA_TIMER, 1000);
    startService(myIntent);

}

IntentService代码如下,之后在需要时可以在此扩展IntentService,这里注意一下IntentService类的构造器的重载,super里面是你的路径名。

package me.androiddemo.canglangwenyue.test;

import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

/**
 * Created by canglangwenyue on 14-12-9.
 */
public class MyIntentService extends IntentService {

private NotificationManager manager;
private String message;
private int mMills;
NotificationCompat.Builder builder;

public MyIntentService() {
    // The super call is required. The background thread that IntentService
    // starts is labeled with the string argument you pass.
    super("me.androiddemo.canglangwenyuet.test");
}

@Override
protected void onHandleIntent(Intent intent) {

    message = intent.getStringExtra(myConstants.EXTRA_MESSAGE);

    mMills = intent.getIntExtra(myConstants.EXTRA_TIMER, myConstants.DEFAULT_TIMER_DURATION);

    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

    String action = intent.getAction();
    issueNotification(intent,message);

    if (action.equals(myConstants.ACTION_PING)) {
        issueNotification(intent,message);
    }else if (action.equals(myConstants.ACTION_SNOOZE)) {
        notificationManager.cancel(myConstants.NOTIFICATION_ID);
        issueNotification(intent,"");
    }else if (action.equals(myConstants.ACTION_DISMISS)) {
        notificationManager.cancel(myConstants.NOTIFICATION_ID);
    }


}

private void issueNotification(Intent intent, String msg) {
    manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    Intent dissmissItent = new Intent(this,MyIntentService.class);
    dissmissItent.setAction(myConstants.ACTION_DISMISS);
    PendingIntent disIntent = PendingIntent.getService(this,0,dissmissItent,0);

    Intent snoozeIntent = new Intent(this,MyIntentService.class);
    snoozeIntent.setAction(myConstants.ACTION_SNOOZE);
    PendingIntent snoopIntent = PendingIntent.getService(this,0,snoozeIntent,0);

    builder = new NotificationCompat.Builder(this)
            .setSmallIcon(R.drawable.ic_launcher)
            .setContentTitle("Information")
            .setContentText("lalallalala")
            .setDefaults(Notification.DEFAULT_ALL)
            .setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
            .addAction(R.drawable.ic_launcher, "Dismiss", disIntent)
            .addAction(R.drawable.ic_launcher,"snooze",snoopIntent);


    Intent resultIntent = new Intent(this,MainActivity2.class);
    resultIntent.putExtra(myConstants.EXTRA_MESSAGE,msg);
    resultIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);


    PendingIntent resultPendingIntent = PendingIntent.getActivity(this,0,resultIntent,PendingIntent.FLAG_UPDATE_CURRENT);

    builder.setContentIntent(resultPendingIntent);
    //        manager.notify(myConstants.NOTIFICATION_ID,builder.build());
    startTimer(mMills);



}

private void startTimer(int mMills) {
    try {

        Thread.sleep(mMills);

    }catch (Exception e) {
        Log.d(myConstants.DEBUG_TAG, "ERROR");
    }
    issueNotification(builder);
}

private void issueNotification(NotificationCompat.Builder builder) {
    manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

    manager.notify(myConstants.NOTIFICATION_ID,builder.build());
}
}

这里只是实现了一个简单的Timer。参考一下就好。

5.创建一个自定义的notification,只要通过RemoteViews来获取你的XML布局文件,并通过.setContent(remoteViews)就好,大家只需要对比不同之处。

Alt text

 /**
 * 自定义notification样式
 *
 * @param view
 */
public void Cus_Notification(View view) {

    Toast.makeText(MainActivity.this, "AHa", Toast.LENGTH_LONG).show();
    /*实例化NotificationManager以获取系统服务*/
    manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.customnotification);

    remoteViews.setImageViewResource(R.id.imageView, R.drawable.psb);
    remoteViews.setTextViewText(R.id.textView, "Your Haven");

    remoteViews.setTextViewText(R.id.textView2, "YUI");
    remoteViews.setTextViewText(R.id.textView3, "豆瓣-FNM -我的红心 MHZ");

    remoteViews.setViewVisibility(R.id.my_large_button, View.VISIBLE);
    notifyBuilder = new NotificationCompat.Builder(this)
            .setContent(remoteViews)
            .setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL))
             /*设置small icon*/
            .setSmallIcon(R.drawable.ic_launcher)
            /*设置title*/
            .setContentTitle("通知")
            /*设置详细文本*/
            .setTicker("Your Haven")
            .setContentText("Hello world")
            .setWhen(System.currentTimeMillis())
            .setPriority(Notification.PRIORITY_DEFAULT)// 设置该通知优先级
            .setOngoing(true);

    Notification noty = notifyBuilder.build();
    noty.contentView = remoteViews;
    manager.notify(313, noty);

}

这里只是添加了一个ImageView和三个TextView,大家可以进一步扩充,例如添加ImageButton,并通过BroadcastReceiver来监听。

6.最后在写一个带进度条指示器的notification吧,到了这里大家应该理解什么叫求同存异了吧,哈哈。至于其它的notification,例如FullScreen下的notification或者LockScreen的notification等,都是换汤不换药,大家只要按需设置相应的属性就好,具体可以查询官方文档。

Alt text

/**
 * 有进度条的notification
 * @param view
 */
public void Pro_Notification(View view) {

    manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    notifyBuilder = new NotificationCompat.Builder(this);
    notifyBuilder.setContentTitle("Picture Download")
            .setContentText("Download in progress")
            .setOngoing(true)
            .setSmallIcon(R.drawable.ic_launcher);
// Start a lengthy operation in a background thread
    new Thread(
            new Runnable() {
                @Override
                public void run() {
                    int incr;
                    // Do the "lengthy" operation 20 times
                    for (incr = 0; incr <= 100; incr += 5) {
                        // Sets the progress indicator to a max value, the
                        // current completion percentage, and "determinate"
                        // state
                        notifyBuilder.setProgress(100, incr, false);
                        // Displays the progress bar for the first time.
                        manager.notify(0, notifyBuilder.build());
                        // Sleeps the thread, simulating an operation
                        // that takes time
                        try {
                            // Sleep for 5 seconds
                            Thread.sleep(5 * 1000);
                        } catch (InterruptedException e) {
                            Log.d("NOTIFICATION", "sleep failure");
                        }
                    }
                    // When the loop is finished, updates the notification
                    notifyBuilder.setContentText("Download complete")
                            // Removes the progress bar
                            .setProgress(0, 0, false);
                    manager.notify(213, notifyBuilder.build());
                }
            }
// Starts the thread by calling the run() method in its Runnable
    ).start();

}

大概解释一下,就是在一个异步线程里面每隔5秒使notification的process增加5,循环结束后,在重新添加一个notification来显示下载完成。至于进度还有其他方式设置,这里就不一一介绍了,实际使用时可以传入具体的下载进度。

好了,感觉已经写了好多了,最后来总结一下notification的使用吧。
(一)通过以下几个步骤就可以获得一个notification的实现:

1.获取NotificationManager实例,即获得状态通知栏管理权;

/*实例化NotificationManager以获取系统服务*/
   manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

2.实例化notification的constructor

notifyBuilder = new NotificationCompat.Builder(this)

3.对notifyBuilder设置各种属性

/**
            * 前三个属性必须设置
            */
           /*设置small icon*/
           .setSmallIcon(R.drawable.ic_launcher)
           /*设置title*/
           .setContentTitle("通知")
           /*设置详细文本*/
           .setContentText("Hello world")
            /*设置发出通知的时间为发出通知时的系统时间*/
           .setWhen(System.currentTimeMillis())
            /*设置发出通知时在status bar进行提醒*/
           .setTicker("来自问月的祝福")
            /*设置点击后通知消失*/
           .setAutoCancel(true)
                   /**
                    * 设置
                    notification的默认效果有以下几种
                    Notification.DEFAULT_ALL:铃声、闪光、震动均系统默认。
                    Notification.DEFAULT_SOUND:系统默认铃声。
                    Notification.DEFAULT_VIBRATE:系统默认震动。
                    Notification.DEFAULT_LIGHTS:系统默认闪光。
                    */
           .setDefaults(Notification.DEFAULT_VIBRATE)
            /*setOngoing(boolean)设为true,notification将无法通过左右滑动的方式清除
           * 可用于添加常驻通知,必须调用cancle方法来清除
           */
           .setOngoing(true)
            /*设置通知数量的显示类似于QQ那种,用于同志的合并*/
           .setNumber(2);

4.显示notification

manager.notify(100, notifyBuilder.build());

(二)一些常用的属性函数

.setDefaults(int defaults)
用于向通知添加声音、闪灯和振动效果的最简单、使用默认(defaults)属性,具体在上述代码中有写到。

.setVibrate(long[] pattern)
用于设置是否开启震动,true为开启

.setSound(Uri sound)
用于设置通知铃声

.setPriority(int pri)
用于设置优先级,同样之前有详细讲解

.setOngoing(boolean ongoing)
设置为true则为常驻通知栏的通知类似于墨迹天气那种的效果。

setProgress(int max, int progress,boolean indeterminate)
用于设置进度的notification中
嗯,今天就写这些吧,最后附上demo的下载地址。点此下载