/**
* 发送通知到通知栏
*
* @param contentIntent 通知动作
* @param info 通知显示内容
*/
private void showNotification(PendingIntent contentIntent, Notice info) {
//2、发布到通知栏,让监听者能点击
//获取NotificationManager实例
NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//实例化NotificationCompat.Builde并设置相关属性
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentIntent(contentIntent)
.setDefaults(Notification.DEFAULT_ALL)
//设置小图标
.setSmallIcon(R.mipmap.ic_launcher)
//设置通知标题
.setContentTitle(AppConstants.getNotifyTypeName(info.getNotifyType()))
.setAutoCancel(true)
//设置通知内容
.setContentText(info.getIdentify());
//通过builder.build()方法生成Notification对象,并发送通知,id=1
notifyManager.notify(1, builder.build());
}
//NotificationManagerService#enqueueNotificationWithTag()
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
copy, idOut, user.getIdentifier());
void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
final int callingPid, final String tag, final int id, final Notification notification,
int[] idOut, int incomingUserId) {
if (DBG) {
Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
+ " notification=" + notification);
}
checkCallerIsSystemOrSameApp(pkg);
final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
//除了系统的通知和已注册的监听器允许入队列外,其他 app 的通知都会限制通知数上限和通知频率上限;
// Limit the number of notifications that any given package except the android
// package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationList) {
final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
if (appEnqueueRate > mMaxPackageEnqueueRate) {
mUsageStats.registerOverRateQuota(pkg);
final long now = SystemClock.elapsedRealtime();
if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
+ ". Shedding events. package=" + pkg);
mLastOverRateLogTime = now;
}
return;
}
// Whitelist pending intents.将 notification 的 PendingIntent 加入到白名单
if (notification.allPendingIntents != null) {
final int intentCount = notification.allPendingIntents.size();
if (intentCount > 0) {
final ActivityManagerInternal am = LocalServices
.getService(ActivityManagerInternal.class);
final long duration = LocalServices.getService(
DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
for (int i = 0; i < intentCount; i++) {
PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
if (pendingIntent != null) {
am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);
}
}
}
}
//将之前的 notification 进一步封装为 StatusBarNotification 和 NotificationRecord,最后封装到一个异步线程 EnqueueNotificationRunnable 中
// setup local book-keeping
final StatusBarNotification n = new StatusBarNotification(
pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
user);
final NotificationRecord r = new NotificationRecord(getContext(), n);
//mHandler 是 WorkerHandler 类的一个实例,在 NotificationManagerService#onStart() 方法中被创建
mHandler.post(new EnqueueNotificationRunnable(userId, r));
在 EnqueueNotificationRunnable中, mRankingHelper.sort(mNotificationList);//对所有 notification 进行重新排序
mListeners.notifyPostedLocked(n, oldSbn); 执行由应用端实现的通知回调。
最后发出声音 :buzzBeepBlinkLocked
}
public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
// Lazily initialized snapshots of the notification.
TrimCache trimCache = new TrimCache(sbn);
for (final ManagedServiceInfo info : mServices) {
boolean sbnVisible = isVisibleToListener(sbn, info);
boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
// This notification hasn't been and still isn't visible -> ignore.
if (!oldSbnVisible && !sbnVisible) {
continue;
}
final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
// This notification became invisible -> remove the old one.
if (oldSbnVisible && !sbnVisible) {
final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
mHandler.post(new Runnable() {
@Override
public void run() {
notifyRemoved(info, oldSbnLightClone, update);
}
});
continue;
}
final StatusBarNotification sbnToPost = trimCache.ForListener(info);
mHandler.post(new Runnable() {
@Override
public void run() {
notifyPosted(info, sbnToPost, update);
}
});
}
private void notifyPosted(final ManagedServiceInfo info,
final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
final INotificationListener listener = (INotificationListener)info.service;
StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
try {//listener 类型是 NotificationListenerService.NotificationListenerWrapper 的代理对象。
listener.onNotificationPosted(sbnHolder, rankingUpdate);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
}
}
SystemUI渲染展示Notification,SystemUI系统app中BaseStatubar类注册NotificationListenerService 服务回调onNotificationPosted
*/
public void onNotificationPosted(StatusBarNotification sbn) {
// optional
}
MyNotificationListenService 负责监听消息
import java.lang.reflect.Field;
public class MyNotificationListenService extends NotificationListenerService {
private static final String TAG = MyNotificationListenService.class.getSimpleName();
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
super.onNotificationPosted(sbn);
Log.d(TAG, "onNotificationPosted");
Notification n = sbn.getNotification();
if (n == null) {
return;
}
// 标题和时间
String title = "";
if (n.tickerText != null) {
title = n.tickerText.toString();
}
long when = n.when;
// 其它的信息存在一个bundle中,此bundle在android4.3及之前是私有的,需要通过反射来获取;android4.3之后可以直接获取
Bundle bundle = null;
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR2) {
// android 4.3
try {
Field field = Notification.class.getDeclaredField("extras");
bundle = (Bundle) field.get(n);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
// android 4.3之后
bundle = n.extras;
}
// 内容标题、内容、副内容
String contentTitle = bundle.getString(Notification.EXTRA_TITLE);
if (contentTitle == null) {
contentTitle = "";
}
String contentText = bundle.getString(Notification.EXTRA_TEXT);
if (contentText == null) {
contentText = "";
}
String contentSubtext = bundle.getString(Notification.EXTRA_SUB_TEXT);
if (contentSubtext == null) {
contentSubtext = "";
}
Log.i(TAG, "notify msg: title=" + title + " ,when=" + when
+ " ,contentTitle=" + contentTitle + " ,contentText="
+ contentText + " ,contentSubtext=" + contentSubtext);
}
@Override
public void onNotificationRankingUpdate(RankingMap rankingMap) {
Log.w(TAG, "onNotificationRankingUpdate");
Message.obtain(sHandler, MSG_ORDER,
new Delta(null, rankingMap)).sendToTarget();
}
@Override
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
Log.w(TAG, "onNotificationPosted: " + sbn.getKey());
Message.obtain(sHandler, MSG_NOTIFY,
new Delta(sbn, rankingMap)).sendToTarget();
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
Log.w(TAG, "onNotificationRemoved: " + sbn.getKey());
Message.obtain(sHandler, MSG_CANCEL,
new Delta(sbn, rankingMap)).sendToTarget();
}
}