通常,在手机有未接电话,收到消息或者挂着退出主界面的QQ,在状态栏会有一个Notification,那么,这个notification如何产生的?
通常做法:
Intentintent=newIntent();
intent.setClass(this,Noti.class);
//一般而言,对于需要点击Notification需要迁移到对应的View的需要下面这个操作
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntentmPendingIntent=PendingIntent.getActivity(this,0,intent,0);
NotificationmNotification=newNotification();
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类,这个类从根本上讲只是一个记录我们需要在状态栏显示Notificationicon的一些信息,比如:要显示的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:
publicvoidsetLatestEventInfo(Contextcontext,
CharSequencecontentTitle,CharSequencecontentText,PendingIntentcontentIntent){
RemoteViewscontentView=newRemoteViews(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函数:
publicvoidnotify(Stringtag,intid,Notificationnotification)
{
int[]idOut=newint[1];
INotificationManagerservice=getService();
Stringpkg=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:idcorrupted:sent"+id+",gotback"+idOut[0]);
}
}catch(RemoteExceptione){
}
}
最重要的是,在NotificationManagerService.java中,service.enqueueNotificationWithTag(pkg,tag,id,notification,idOut);它调用NotificationManagerService中方法enqueueNotificationInternal发出声音振动和灯光,这都只要调用相关的系统服务做就可以了。
以何种方式进行通知状态栏显示的?
if(notification.icon!=0){
StatusBarNotificationn=newStatusBarNotification(pkg,id,tag,
r.uid,r.initialPid,notification);
if(old!=null&&old.statusBarKey!=null){
r.statusBarKey=old.statusBarKey;
longidentity=Binder.clearCallingIdentity();
try{
mStatusBar.updateNotification(r.statusBarKey,n);
}
finally{
Binder.restoreCallingIdentity(identity);
}
}else{
longidentity=Binder.clearCallingIdentity();
try{
r.statusBarKey=mStatusBar.addNotification(n);
mAttentionLight.pulse();
}
finally{
Binder.restoreCallingIdentity(identity);
}
}
sendAccessibilityEvent(notification,pkg);
在这里调用了statusbarService.java的updateNotification方法,又调用addNotificationViews将icon显示到状态栏并显示相关信息。
updateNotification这个方法中调用addNotificationViews:
//Constructtheicon.
finalStatusBarIconViewiconView=newStatusBarIconView(this,
notification.pkg+"/0x"+Integer.toHexString(notification.id));
finalStatusBarIconic=newStatusBarIcon(notification.pkg,notification.notification.icon,
notification.notification.iconLevel,notification.notification.number);
if(!iconView.set(ic)){
handleNotificationError(key,notification,"Couldingcreateicon:"+ic);
returnnull;
}
至此,就完成了添加一个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下privateclassLauncherimplementsView.OnClickListener
是监听点击StatusBarService的函数,当点击这个Notification时,在voidOnClick()方法中有这样的callback:mBarService.onNotificationClick(mPkg,mTag,mId);
mBarService是这样定义的:
IStatusBarServicemBarService=IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
这就意味着mBarService使用的是StatusbarManagerService.java中定义的接口,如下:
publicinterfaceNotificationCallbacks{
voidonSetDisabled(intstatus);
voidonClearAll();
voidonNotificationClick(Stringpkg,Stringtag,intid);
voidonPanelRevealed();
voidonNotificationError(Stringpkg,Stringtag,intid,
intuid,intinitialPid,Stringmessage);
}
onNotificationClick是在NotificationManagerService.java中实现的。因此,当点击statusbar上的Notification时就调用NotificationManagerService中的这个响应函数:
publicvoidonNotificationClick(Stringpkg,Stringtag,intid){
cancelNotification(pkg,tag,id,Notification.FLAG_AUTO_CANCEL,
Notification.FLAG_FOREGROUND_SERVICE);
}
cancelNotification定义如下:
privatevoidcancelNotification(Stringpkg,Stringtag,intid,intmustHaveFlags,
intmustNotHaveFlags){
EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL,pkg,id,mustHaveFlags);
synchronized(mNotificationList){
intindex=indexOfNotificationLocked(pkg,tag,id);
if(index>=0){
NotificationRecordr=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。