时序图
通知的发送是通过NotificationManager的notify()方法:
NotificationManger->notify()
public void notify(int id, Notification notification)
{
notify(null, id, notification);
}
public void notify(String tag, int id, Notification notification)
{
int[] idOut = new int[1];
INotificationManager service = getService();
String pkg = mContext.getPackageName();
...
try {
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
stripped, idOut, UserHandle.myUserId());
if (id != idOut[0]) {
Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
}
} catch (RemoteException e) {
}
}
/** @hide */
static public INotificationManager getService()
{
if (sService != null) {
return sService;
}
IBinder b = ServiceManager.getService("notification");
sService = INotificationManager.Stub.asInterface(b);
return sService;
}
可以明显示的看到上面代码中的getService()通过Binder获取到NotificationManager对应的Service,按Android系统中的命令惯例即是 NotificationManagerService, 真正的处理在此Service中。
NotificationManagerService->enqueueNotificationWithTag()
enqueueNotificationWithTag() -> enqueueNotificationInternal():
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) {
...
//检测是否是同一APP发出的,是个通知安全检查,有问题则抛出异常。
checkCallerIsSystemOrSameApp(pkg);
final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); //是否为系统通知
final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg); //所有系统服务与应用已注册的服务。
...
if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationList) {
int count = 0;
final int N = mNotificationList.size();
for (int i=0; i<N; i++) {
...
count++;
//最大50条
if (count >= MAX_PACKAGE_NOTIFICATIONS) {
Slog.e(TAG, "Package has already posted " + count
+ " notifications. Not showing more. package=" + pkg);
return;
}
}
}
}
}
...
if (notification.getSmallIcon() != null) {
if (!notification.isValid()) {
throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg
+ " id=" + id + " notification=" + notification);
}
}
mHandler.post(new Runnable() {
@Override
public void run() {
synchronized (mNotificationList) {
...
// blocked apps 应用被设置不允许弹出通知
if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
if (!isSystemNotification) {
r.score = JUNK_SCORE;
Slog.e(TAG, "Suppressing notification from package " + pkg
+ " by user request.");
mUsageStats.registerBlocked(r);
}
}
if (r.score < SCORE_DISPLAY_THRESHOLD) {
// Notification will be blocked because the score is too low.
return;
}
if (notification.getSmallIcon() != null) {
StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
//通知通知消息观察者有新通知或有通知更新。
mListeners.notifyPostedLocked(n, oldSbn);
} else {
Slog.e(TAG, "Not posting notification without small icon: " + notification); //没图标的通知不显示。
if (old != null && !old.isCanceled) {
mListeners.notifyRemovedLocked(n);
}
...
}
//通知语言与震动相关处理
buzzBeepBlinkLocked(r);
}
}
});
idOut[0] = id;
}
从上面的方法中我们可以得知:
1. 如果当前应用不是系统应用并且不是已注册的服务的话,那么Android系统最多让同时存在50条通知消息。
2. 应用被设置禁止弹出通知或通知没设置图标的话通知也不能被弹出。
NotificationListenerService->notifyPostedLocked()
通知是被上面的mListeners.notifyPostedLocked()->notifyPosted()->INotificationListener->onNotificationPosted()方法通知到各个服务的,INotificationListener对应的处理服务即是NotificationListenerService:
private void notifyPosted(final ManagedServiceInfo info,
final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
final INotificationListener listener = (INotificationListener)info.service;
StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
try {
listener.onNotificationPosted(sbnHolder, rankingUpdate);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
}
}
BaseStatusBar中的NotificationListenerService实例接收通知消息
那么这是在哪里处理的吗?
在Android源码中全局搜索下就可以找到以下代码:
private final NotificationListenerService mNotificationListener =
new NotificationListenerService() {
...
@Override
public void onNotificationPosted(final StatusBarNotification sbn,
final RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
if (sbn != null) {
mHandler.post(new Runnable() {
@Override
public void run() {
...
//// Remove existing notification to avoid stale data.
if (isUpdate) {
removeNotification(key, rankingMap);
} else {
mNotificationData.updateRanking(rankingMap); //排序
}
return;
}
if (isUpdate) {
updateNotification(sbn, rankingMap);
} else {
addNotification(sbn, rankingMap, null /* oldEntry */); //创建新通知
}
}
});
}
}
}
mNotificationListener即是NotificationListenerService的实例,它在BaseStatusBar的start方法中将此mNotificationListener关联到NotificationManagerService中:
//BaseStatusBar.java
public void start() {
// Set up the initial notification state.
try {
mNotificationListener.registerAsSystemService(mContext,
new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
UserHandle.USER_ALL);
} catch (RemoteException e) {
Log.e(TAG, "Unable to register notification listener", e);
}
}
//NotificationListenerService.java
@SystemApi
public void registerAsSystemService(Context context, ComponentName componentName,
int currentUser) throws RemoteException {
mSystemContext = context;
if (mWrapper == null) {
mWrapper = new INotificationListenerWrapper();
}
INotificationManager noMan = getNotificationInterface();
//即上一小节的final INotificationListener listener = (INotificationListener)info.service
noMan.registerListener(mWrapper, componentName, currentUser);
mCurrentUser = currentUser;
}
PhoneStatusBar创建通知视图并显示
BaseStatusBar即是用来处理状态栏相关业务的类,继承BaseStatusBar的有PhoneStatusBar、TvStatusBar,看名字就可以得知PhoneStatusBar是用于手机屏幕而TvStatusBar是用于TV的,再继续看PhoneStatusBar中的处理:
//PhoneStatusBar.java
@Override
public void addNotification(StatusBarNotification notification, RankingMap ranking,
Entry oldEntry) {
...
//创建通知对应的视图
Entry shadeEntry = createNotificationViews(notification);
if (shadeEntry == null) {
return;
}
//收到通知时在屏幕上方弹出的通知提示相关处理。
boolean isHeadsUped = mUseHeadsUp && shouldInterrupt(shadeEntry);
if (isHeadsUped) {
mHeadsUpManager.showNotification(shadeEntry);
// Mark as seen immediately
setNotificationShown(notification);
}
...
addNotificationViews(shadeEntry, ranking);
...
}
上面的处理中可以很明显的看到通过我们在最初调用 NotificationManager.notify() 时创建出的StatusBarNotification来创建出一个用来状态栏通知显示的Entry,里面存有创建好的单个通知视图:
createNotificationViews()->inflateViews():
//BaseStatusBar.java
protected boolean inflateViews(Entry entry, ViewGroup parent) {
PackageManager pmUser = getPackageManagerForUser(
entry.notification.getUser().getIdentifier());
int maxHeight = mRowMaxHeight;
final StatusBarNotification sbn = entry.notification;
RemoteViews contentView = sbn.getNotification().contentView;
...
// create the row view
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
parent, false);
//绑定点击事件处理
mNotificationClicker.register(row, sbn);
...
//调用RemoteViews->apply()方法绑定视图
contentViewLocal = contentView.apply(
sbn.getPackageContext(mContext),
contentContainer,
mOnClickHandler);
...
entry.row.setHeightRange(mRowMinHeight, maxHeight);
...
从上面的代码中可以看到:
1. 每个通知都有个高度范围,64dp-256dp。
2. 通知的layout模板 R.layout.status_bar_notification_row 。
3. PhoneStatusBar收到通知后最终调用RemoteViews->apply()来进行视图一绑定,证实了我们上一篇文章的推测。
后面的就将View加入到StatusBarView的NotificationStackScrollLayout中,StatusBarView是在BaseStatusBar->start()方法中被调用。
来源:oschina
链接:https://my.oschina.net/u/920274/blog/3059022