一句话总结
在SystemUI进程中通过INotificationManager将要休眠的通知传递给SystemServer进程,SystemServer进程中通过AlarmManager定时发送一条广播让NotificationManagerService发送通知。
1.SystemUI进程中处理点击事件
点击通知休眠按钮,由NotificationSnooze的handleCloseControls处理
NotificationSnooze.java
if (mSnoozeListener != null && mSelectedOption != null) {
// Snooze option selected so commit it
mSnoozing = true;
mSnoozeListener.snooze(mSbn, mSelectedOption);
}
经过一系列的转发,最终会调用到StatusBar的setNotificationSnoozed
NotificationStackScrollLayout.java
@Override
public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
}
StatusBar中有一个NotificationListenserService实例mNotificationListener。负责和NotificationManagerService跨进程(SystemServer)通信。NotificationListenserService中有一个内部类NotificationListernerWrapper实现了INotificationListener接口,这个才是具体实现。
StatusBar.java
//一般为null
if (snoozeOption.getSnoozeCriterion() != null) {
mNotificationListener.snoozeNotification(sbn.getKey(),
snoozeOption.getSnoozeCriterion().getId());
} else {
mNotificationListener.snoozeNotification(sbn.getKey(),
snoozeOption.getMinutesToSnoozeFor() * 60 * 1000);
}
接下来我们看一下snoozeNotification方法的具体实现,方法的主要作用是通知NotificationManager休眠该通知。这也就实现了从systemui进程调用到了SystemServer进程
NotificationListenerService.java
/*
* @param key The key of the notification to snooze
* @param durationMs A duration to snooze the notification for, in milliseconds.
* /
public final void snoozeNotification(String key, long durationMs) {
if (!isBound()) return;
try {
getNotificationInterface().snoozeNotificationUntilFromListener(
mWrapper, key, durationMs);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
}
2.SystemServer进程处理通知休眠
getNotificationInterface()方法返回的是NotificationManagerService(以下简称NMS)的客户端,跨进程调用到NMS的snoozeNotificationUntilFromListener。然后会调用到snoozeNotificationInt。
NotificationServiceManager.java
void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
ManagedServiceInfo listener) {
//省略部分代码
// Needs to post so that it can cancel notifications not yet enqueued.
mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
}
可以看到post了一个SnoozeNotificationRunnable。代码比较好理解,根据通知的Key值去找到这个通知,再调用snoozeLocked方法
NotificationManagerService$SnoozeNotificationRunnable.java
public void run() {
synchronized (mNotificationLock) {
final NotificationRecord r = findNotificationByKeyLocked(mKey);
if (r != null) {
snoozeLocked(r);
}
}
}
snoozeLocked方法也比较好理解,如果组合通知和非组合通知分开处理,但最后都是通过调用snoozeNotificationLocked。
NotificationManagerService$SnoozeNotificationRunnable.java
@GuardedBy("mNotificationLock")
void snoozeLocked(NotificationRecord r) {
if (r.sbn.isGroup()) {
//略
} else {
// just snooze the one notification
snoozeNotificationLocked(r);
}
}
这段代码的意思很明显,1.从本地的数据即中移除该通知。2.取消这个通知,让通知从通知栏消失。3.更新通知灯。4.通过SnoozeHelper.snooze休眠通知。
@GuardedBy("mNotificationLock")
void snoozeNotificationLocked(NotificationRecord r) {
boolean wasPosted = removeFromNotificationListsLocked(r);
cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null);
updateLightsLocked();
if (mSnoozeCriterionId != null) {
mAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
mSnoozeHelper.snooze(r);
} else {
mSnoozeHelper.snooze(r, mDuration);
}
savePolicyFile();
}
通知休眠机制的具体实现:通过AlarmManager定时发送广播,在广播中在通过NMS发送休眠的通知
SnoozeHelper.java
/** * Snoozes a notification and schedules an alarm to repost at that time. */
protected void snooze(com.android.server.notification.NotificationRecord record, long duration) {
//记录数据list中
snooze(record);
//安排唤醒时间
scheduleRepost(record.sbn.getPackageName(), record.getKey(), record.getUserId(), duration);
}
private void scheduleRepost(String pkg, String key, int userId, long duration) {
final PendingIntent pi = createPendingIntent(pkg, key, userId);
long time = SystemClock.elapsedRealtime() + duration;
if (DEBUG) Slog.d(TAG, "Scheduling evaluate for " + new Date(time));
//定时发送广播
mAm.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, time, pi);
}
private PendingIntent createPendingIntent(String pkg, String key, int userId) {
//广播
return PendingIntent.getBroadcast(mContext,
REQUEST_CODE_REPOST,
//略
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (REPOST_ACTION.equals(intent.getAction())) {
repost(intent.getStringExtra(EXTRA_KEY), intent.getIntExtra(EXTRA_USER_ID,
UserHandle.USER_SYSTEM));
}
}
};
protected void repost(String key, int userId) {
if (record != null && !record.isCanceled) {
mCallback.repost(userId, record);
}
}