通常,在手机有未接电话,收到消息或者挂着退出主界面的QQ,在状态栏会有一个Notification,那么,这个notification如何产生的?
通常做法:
Intent intent = new Intent();
intent.setClass(this, Noti.class);
//一般而言,对于需要点击Notification需要迁移到对应的View的需要下面这个操作
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent mPendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
Notification mNotification = new Notification();
mNotification.icon = R.drawable.presence_online;//icon 的id
mNotification.tickerText = "Online";
mNotification.defaults = Notification.DEFAULT_SOUND ;
mNotification.flags = Notification.FLAG_AUTO_CANCEL;
mNotification.setLatestEventInfo(this, "QQ", "Online", mPendingIntent);
mNotificationManager.notify(0, mNotification);
在android手机中,有一个Notification类,这个类从根本上讲只是一个记录我们需要在状态栏显示Notification icon的一些信息,比如:要显示的Icon的id,led灯闪烁以及闪烁颜色和闪烁时间,让手机产生振动等。Notification的flag有FLAG_SHOW_LIGHTS,FLAG_AUTO_CANCEL等。有关Notification有三个主要的函数:setLatestEventInfo,notify和cancel
在下面这段代码中,RemoteViews 是显示在扩展状态栏上的,也就是将状态栏拉下时显示的Notification,
contentView.setImageViewResource(com.android.internal.R.id.icon, this.icon)则是将我们设定的icon赋给id为com.android.internal.R.id.icon的ImageView,以便在statusBar中调用显示。同样下面都是将本地信息设置为全局信息以便在statusbar中显示。特别要提一下contentIntent是一个PendingIntent,它负责在点击Notification时迁移的View。Notification.java:
public void setLatestEventInfo(Context context,
CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
RemoteViews contentView = new RemoteViews(context.getPackageName(),
com.android.internal.R.layout.status_bar_latest_event_content);
if (this.icon != 0) {//实际上就是要显示的消息对应的icon
contentView.setImageViewResource(com.android.internal.R.id.icon, this.icon);
}
if (contentTitle != null) {//Notification对应的title
contentView.setTextViewText(com.android.internal.R.id.title, contentTitle);
}
if (contentText != null) {//状态所对应的下标题
contentView.setTextViewText(com.android.internal.R.id.text, contentText);
}
if (this.when != 0) {
contentView.setLong(com.android.internal.R.id.time, "setTime", when);
}
this.contentView = contentView;
this.contentIntent = contentIntent;
}
那么它是如何显示到状态栏上的?
NotificationManager调用Notify函数 :
public void notify(String tag, int id, Notification notification)
{
int[] idOut = new int[1];
INotificationManager service = getService();
String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
try {
service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut);
if (id != idOut[0]) {
Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
}
} catch (RemoteException e) {
}
}
最重要的是,在NotificationManagerService.java中,service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut);它调用NotificationManagerService中方法enqueueNotificationInternal发出声音振动和灯光,这都只要调用相关的系统服务做就可以了。
以何种方式进行通知状态栏显示的?
if (notification.icon != 0) {
StatusBarNotification n = new StatusBarNotification(pkg, id, tag,
r.uid, r.initialPid, notification);
if (old != null && old.statusBarKey != null) {
r.statusBarKey = old.statusBarKey;
long identity = Binder.clearCallingIdentity();
try {
mStatusBar.updateNotification(r.statusBarKey, n);
}
finally {
Binder.restoreCallingIdentity(identity);
}
} else {
long identity = Binder.clearCallingIdentity();
try {
r.statusBarKey = mStatusBar.addNotification(n);
mAttentionLight.pulse();
}
finally {
Binder.restoreCallingIdentity(identity);
}
}
sendAccessibilityEvent(notification, pkg);
在这里调用了statusbarService.java的updateNotification方法,又调用addNotificationViews将icon显示到状态栏并显示相关信息。
updateNotification这个方法中调用addNotificationViews:
// Construct the icon.
final StatusBarIconView iconView = new StatusBarIconView(this,
notification.pkg + "/0x" + Integer.toHexString(notification.id));
final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,
notification.notification.iconLevel, notification.notification.number);
if (!iconView.set(ic)) {
handleNotificationError(key, notification, "Coulding create icon: " + ic);
return null;
}
至此,就完成了添加一个icon到Statusbar,同时还有text、title等等。
以DownLoadProvider下载完成后点击下载的Notification,然后消失Notification为例解释FLAG_AUTO_CANCEL原因:
在DownLoadProvider这个app中,DownLoadService会创建一个用于更新下载完成Notification的类DownloadNotification,还有一个开启的线程:updateThread,在这个线程中调用了mNofier.updateNofication(mDownloads.values());然后再DownloadNotification.java中更新下载进度和完成状态,当下载完成时会在StatusBar上显示下载完成这个icon。
那么,系统如何维护状态栏上的Notification呢?也就是如何点击后取消这个Notification呢?
在StatusBarService下private class Launcher implements View.OnClickListener
是监听点击StatusBarService的函数,当点击这个Notification时,在void OnClick()方法中有这样的callback:mBarService.onNotificationClick(mPkg, mTag, mId);
mBarService是这样定义的:
IStatusBarService mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
这就意味着mBarService使用的是StatusbarManagerService.java中定义的接口,如下:
public interface NotificationCallbacks {
void onSetDisabled(int status);
void onClearAll();
void onNotificationClick(String pkg, String tag, int id);
void onPanelRevealed();
void onNotificationError(String pkg, String tag, int id,
int uid, int initialPid, String message);
}
onNotificationClick是在NotificationManagerService.java中实现的。因此,当点击statusbar上的Notification时就调用NotificationManagerService中的这个响应函数:
public void onNotificationClick(String pkg, String tag, int id) {
cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
Notification.FLAG_FOREGROUND_SERVICE);
}
cancelNotification定义如下:
private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
int mustNotHaveFlags) {
EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags);
synchronized (mNotificationList) {
int index = indexOfNotificationLocked(pkg, tag, id);
if (index >= 0) {
NotificationRecord r = mNotificationList.get(index);
if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
return;
}
if ((r.notification.flags & mustNotHaveFlags) != 0) {
return;
}
mNotificationList.remove(index);
cancelNotificationLocked(r);
updateLightsLocked();
}
}
这就解释了为什么只有设置Notification 的flag为Notification.FLAG_AUTO_CANCEL才能取消Notification。