今天在复习Notification通知栏的时候,好奇翻了一下源码,终于弄懂了PendingIntent在Notification中的机制以及工作流程,这对我研究Activity的启动流程有了一定的帮助。
通知的实现
我们复习一下常规的通知栏是怎么样实现的:
//声明一个新的Notification,在里面设置参数
Notification notification = new Notification();
notification.icon = R.drawable.ic_launcher;
notification.tickerText = "hello world";
notification.when = System.currentTimeMillis();
notification.flags = Notification.FLAG_AUTO_CANCEL;
Intent intent = new Intent(this, DemoActivity_2.class);
//PendingIntent是延时Intent是指当点击通知栏之后弹出intent指向activity
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//这里是指给予通知栏一个标准布局
notification.setLatestEventInfo(this, "chapter_5", "this is notification.", pendingIntent);
//声明一个NotificationManager
NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
//根据ID发送
manager.notify(sId, notification);
第一步:声明好Notification之后,我们要在notification中设置好参数,比如图标,text,时间,flag等等。
第二步:声明一个Intent,接着将Intent放入PendingIntent中。这个时候PendingIntent中我们要将Intent封装如PendingIntent中。
第三步:声明一个NotificationManager之后,调用notify(int id,Notification n)来发送通知请求。
在这里我们可以清晰的知道NotificationManager其实就是发送notification通知相关的参数出发送端的进程之外,那么这里一定用到了进程间的通讯Binder,而发送的数据notification一定是序列化。
public class Notification implements Parcelable
下面是部分参数源码:
/**
* A timestamp related to this notification, in milliseconds since the epoch.
*
* Default value: {@link System#currentTimeMillis() Now}.
*
* Choose a timestamp that will be most relevant to the user. For most finite events, this
* corresponds to the time the event happened (or will happen, in the case of events that have
* yet to occur but about which the user is being informed). Indefinite events should be
* timestamped according to when the activity began.
*
* Some examples:
*
* <ul>
* <li>Notification of a new chat message should be stamped when the message was received.</li>
* <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
* <li>Notification of a completed file download should be stamped when the download finished.</li>
* <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
* <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
* <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
* </ul>
*
*/
public long when;
/**
* The resource id of a drawable to use as the icon in the status bar.
* This is required; notifications with an invalid icon resource will not be shown.
*/
public int icon;
/**
* If the icon in the status bar is to have more than one level, you can set this. Otherwise,
* leave it at its default value of 0.
*
* @see android.widget.ImageView#setImageLevel
* @see android.graphics.drawable.Drawable#setLevel
*/
public int iconLevel;
/**
* The number of events that this notification represents. For example, in a new mail
* notification, this could be the number of unread messages.
*
* The system may or may not use this field to modify the appearance of the notification. For
* example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
* superimposed over the icon in the status bar. Starting with
* {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
* {@link Notification.Builder} has displayed the number in the expanded notification view.
*
* If the number is 0 or negative, it is never shown.
*/
public int number;
/**
* The intent to execute when the expanded status entry is clicked. If
* this is an activity, it must include the
* {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
* that you take care of task management as described in the
* <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
* Stack</a> document. In particular, make sure to read the notification section
* <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
* Notifications</a> for the correct ways to launch an application from a
* notification.
*/
public PendingIntent contentIntent;
/**
* The intent to execute when the notification is explicitly dismissed by the user, either with
* the "Clear All" button or by swiping it away individually.
*
* This probably shouldn't be launching an activity since several of those will be sent
* at the same time.
*/
public PendingIntent deleteIntent;
/**
* The view that will represent this notification in the expanded status bar.
*/
public RemoteViews contentView;
...
上面这些就是notification要通过Binder传输到AMS的数据。那么对应的,如果我们想要自定义一个通知栏的话,其实就是将相关的View数据通过RemoteView的contentView(上面的注释明确的写出了这个View将代表了通知在状态栏上)通信到AMS中。这样子就可以办到自定义的通知栏了。
相对应的setLastEventInfo是设置默认模板的通知样式,下面是源码:
public void setLatestEventInfo(Context context,
CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
Notification.Builder builder = new Notification.Builder(context);
// First, ensure that key pieces of information that may have been set directly
// are preserved
builder.setWhen(this.when);
builder.setSmallIcon(this.icon);
builder.setPriority(this.priority);
builder.setTicker(this.tickerText);
builder.setNumber(this.number);
builder.setColor(this.color);
builder.mFlags = this.flags;
builder.setSound(this.sound, this.audioStreamType);
builder.setDefaults(this.defaults);
builder.setVibrate(this.vibrate);
// now apply the latestEventInfo fields
if (contentTitle != null) {
builder.setContentTitle(contentTitle);
}
if (contentText != null) {
builder.setContentText(contentText);
}
builder.setContentIntent(contentIntent);
builder.buildInto(this);
}
在Notification的Builder内部类中已经将通知的默认模板给设定好了。
自定义通知栏
所谓自定义通知栏知道原理之后,我们就很好的实现这个自定义的通知栏了。我们先自定义一个布局文件出来。
下面是布局文件xml的配置:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/light_green"
android:padding="10dp"
android:gravity="center"
android:orientation="horizontal" >
<ImageView
android:id="@+id/icon"
android:background="#000000"
android:layout_width="40dp"
android:scaleType="centerInside"
android:layout_height="40dp" />
<LinearLayout
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right"
android:orientation="vertical" >
<TextView
android:id="@+id/msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:textColor="@android:color/white"
android:visibility="visible"
android:text="TextView" />
<TextView
android:id="@+id/open_activity2"
android:padding="2dp"
android:background="@drawable/common_bkg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:text="open DemoActivity_2" />
</LinearLayout>
</LinearLayout>
配置好了之后,我们来实现notification中的参数:
//声明一个新的Notification,在里面设置参数
Notification notification = new Notification();
notification.icon = R.drawable.ic_launcher;
notification.tickerText = "hello world";
notification.when = System.currentTimeMillis();
notification.flags = Notification.FLAG_AUTO_CANCEL;
Intent intent = new Intent(this, DemoActivity_1.class);
intent.putExtra("sid", "" + sId);
//PendingIntent是延时Intent是指当点击通知栏之后启动intent指向activity
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
这里和前面相似,接下来我们要透过RemoteView来重新设置整个通知的View:
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_notification);
remoteViews.setTextViewText(R.id.msg, "chapter_5: " + sId);
remoteViews.setImageViewResource(R.id.icon, R.drawable.icon1);
//PendingIntent是延时Intent是指当点击通知栏之后启动intent指向activity
PendingIntent openActivity2PendingIntent = PendingIntent.getActivity(this,
0, new Intent(this, DemoActivity_2.class), PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.open_activity2, openActivity2PendingIntent);
//设置contentview
notification.contentView = remoteViews;
//设置pendingItent
notification.contentIntent = pendingIntent;
NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(sId, notification);
这样子就完成了自定义通知栏的实现了,而RemoteView的工作原理之后再另外说一下吧。
下一章将总结一下,之前明白的PendingIntent在Notification中工作原理。
感谢郭霖大神和任玉刚大神的博客和书籍。