我们知道,当手机有通知时,下拉通知中心中会显示所有的通知,该功能是在SystemUI中实现的,接着上篇文章Android11 SystemUI解析 ,本文对通知相关的功能逻辑进行分析,基于Android11 CarSystemUI的通知功能逻辑展开分析。
关于通知功能逻辑,简单来说,无非就是四步,注册、发送、接收、显示,那么接下来针对以上四步进行源码详细分析。
一.注册
关于CarSystemUI启动及相关逻辑可以参考文章Android11 SystemUI解析 ,本文就不赘述了,直接以NotificationPanelViewController类为入口进行分析:
1.NotificationPanelViewController
从构造方法来看:
@Inject
public NotificationPanelViewController(xxxxxxxx,
/* Things needed for notifications */
IStatusBarService barService,
CommandQueue commandQueue,
NotificationDataManager notificationDataManager,
CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper,
CarNotificationListener carNotificationListener,
NotificationClickHandlerFactory notificationClickHandlerFactory,
xxxxxxxxxxx
) {
}
mNotificationViewController = new NotificationViewController(
mNotificationView,PreprocessingManager.getInstance(mContext),
mCarNotificationListener,mCarUxRestrictionManagerWrapper,
mNotificationDataManager);
mNotificationViewController.enable();
可以看到,在创建以上实例时,会通过Inject的方式来创造对应参数的实例,该功能是通过dagger2来实现,具体对应的Module为CarNotificationModule类,看一下CarNotificationListener实例创造时的实现,关于NotificationViewController后面再分析:
@Provides
@Singleton
static CarNotificationListener provideCarNotificationListener(Context context,
CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper,
CarHeadsUpNotificationManager carHeadsUpNotificationManager,
NotificationDataManager notificationDataManager) {
CarNotificationListener listener = new CarNotificationListener();
listener.registerAsSystemService(context, carUxRestrictionManagerWrapper,
carHeadsUpNotificationManager, notificationDataManager);
return listener;
}
可以看到,在provideCarNotificationListener()提供CarNotificationListener实例时,还执行了registerAsSystemService()方法,接下来看一下CarNotificationListener类:
2.CarNotificationListener
public class CarNotificationListener extends NotificationListenerService implements
CarHeadsUpNotificationManager.OnHeadsUpNotificationStateChange {
.....................
.....................
public void registerAsSystemService(Context context,
CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper,
CarHeadsUpNotificationManager carHeadsUpNotificationManager,
NotificationDataManager notificationDataManager) {
try {
mNotificationDataManager = notificationDataManager;
registerAsSystemService(context,
new ComponentName(context.getPackageName(), getClass().getCanonicalName()),
ActivityManager.getCurrentUser());
...................................
} catch (RemoteException e) {
Log.e(TAG, "Unable to register notification listener", e);
}
}
@Override
public void onListenerConnected() {
mActiveNotifications = Stream.of(getActiveNotifications()).collect(
Collectors.toMap(StatusBarNotification::getKey, sbn -> new AlertEntry(sbn)));
mRankingMap = super.getCurrentRanking();
}
}
CarNotificationListener继承了NotificationListenerService类,该类继承了Service,是framework内部的组成部分,通过registerAsSystemService()来看一下该类的实现:
3.NotificationListenerService
public void registerAsSystemService(Context context, ComponentName componentName,
int currentUser) throws RemoteException {
if (mWrapper == null) {
mWrapper = new NotificationListenerWrapper();
}
mSystemContext = context;
INotificationManager noMan = getNotificationInterface();
mHandler = new MyHandler(context.getMainLooper());
mCurrentUser = currentUser;
noMan.registerListener(mWrapper, componentName, currentUser);
}
该方法内部主要执行了两项操作:
a.创建了NotificationListenerWrapper对象,该类继承了INotificationListener.Stub,主要用来接收回调,后面在显示环节进行详细分析;
protected class NotificationListenerWrapper extends INotificationListener.Stub {
@Override
public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
NotificationRankingUpdate update) {
}
@Override
public void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder,
NotificationRankingUpdate update, NotificationStats stats, int reason) {
}
.....................
}
b.将以上对象作为参数通过INotificationManager的registerListener进行注册;
protected final INotificationManager getNotificationInterface() {
if (mNoMan == null) {
mNoMan = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
}
return mNoMan;
}
通过getNotificationInterface()的是实现可以知道,registerListener()执行到了NotificationManagerService里面去了,接下来一起看一下:
4.NotificationManagerService
@Override
public void registerListener(final INotificationListener listener,
final ComponentName component, final int userid) {
enforceSystemOrSystemUI("INotificationManager.registerListener");
mListeners.registerSystemService(listener, component, userid);
}
mListeners是NotificationListeners实例,是在init()中进行初始化的,NotificationListeners是其内部类,看一下具体实现:
public class NotificationListeners extends ManagedServices {
.......................
.......................
@Override
public void onServiceAdded(ManagedServiceInfo info) {
final INotificationListener listener = (INotificationListener) info.service;
final NotificationRankingUpdate update;
synchronized (mNotificationLock) {
update = makeRankingUpdateLocked(info);
updateUriPermissionsForActiveNotificationsLocked(info, true);
}
try {
listener.onListenerConnected(update);
}
}
}
NotificationListeners继承了ManagedServices,registerSystemService方法是在ManagedServices里面实现的,看一下:
public void registerSystemService(IInterface service, ComponentName component, int userid) {
checkNotNull(service);
ManagedServiceInfo info = registerServiceImpl(
service, component, userid, Build.VERSION_CODES.CUR_DEVELOPMENT);
if (info != null) {
onServiceAdded(info);
}
}
private ManagedServiceInfo registerServiceImpl(final IInterface service,
final ComponentName component, final int userid, int targetSdk) {
ManagedServiceInfo info = newServiceInfo(service, component, userid,
true /*isSystem*/, null /*connection*/, targetSdk);
return registerServiceImpl(info);
}
private ManagedServiceInfo registerServiceImpl(ManagedServiceInfo info) {
synchronized (mMutex) {
try {
info.service.asBinder().linkToDeath(info, 0);
mServices.add(info);
return info;
}
}
return null;
}
根据调用关系,registerServiceImpl()方法内先将前面创建的INotificationListener(mWrapper)作为参数创建了ManagedServiceInfo实例info,然后执行linkToDeath进行死亡监测,最后将info加入mServices中进行管理,执行完后再执行onServiceAdded(info),该方法是在NotificationListeners类内部实现的,再回去看一下该方法,最终会调用到CarNotificationListener.java里面的onListenerConnected()方法。
以上注册过程逻辑比较绕,用一张图来总结一下:
二.发送
发送过程比较简单,按照系统提供的方法来发送即可,主要涉及NotificationChannel、Notification、NotificationManager这三个类,简单看一下:
首先某个应用在发送通知前需要创建该应用对应的NotificationChannel,然后在通知中传入对应channel ID就可以了,创建如下:
1.NotificationChannel
public NotificationChannel(String id, CharSequence name, @Importance int importance) {
this.mId = getTrimmedString(id);
this.mName = name != null ? getTrimmedString(name.toString()) : null;
this.mImportance = importance;
}
在创建NotificationChannel时需要传入唯一的id、name和importance,创建如下:
NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
NotificationChannel notificationChannel = new NotificationChannel(DEBUG_CHANNEL_ID, DEBUG_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(notificationChannel);
2.Notification
创建完NotificationChannel后,再创建Notification,Notification创建采用的是Builder模式,主要涉及的内容比较多,创建如下:
Notification missedCallNotification = new Notification.Builder(context, DEBUG_CHANNEL_ID)
.setContentTitle("18330656010")
.setContentText("未接来电")
.setCategory(Notification.CATEGORY_MISSED_CALL)
.setSmallIcon(R.drawable.notification_missed_call)
.setOngoing(true)
.setLargeIcon(Icon.createWithResource(context, R.drawable.notification_missed_call))
.build();
Notification涉及的内容比较多,可以根据需要进行设定;
3.NotificationManager
创建完Notification后,通过NotificationManger来进行发送就可以了:
notificationManager.notify(MISSED_CALL_ID, missedCallNotification);
执行完notify后续的逻辑处理过程,在接收环节进行分析;
三.接收
1.NotificationManager
发送时会调用到notify()方法:
public void notify(int id, Notification notification){
notify(null, id, notification);
}
public void notify(String tag, int id, Notification notification){
notifyAsUser(tag, id, notification, mContext.getUser());
}
跟随调用关系,会调用到notifyAsUser()方法:
public void notifyAsUser(String tag, int id, Notification notification, UserHandle user){
INotificationManager service = getService();
String pkg = mContext.getPackageName();
try {
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
fixNotification(notification), user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
在notifyAsUser()会调用到NotificationManagerService中的enqueueNotificationWithTag()方法,先看一下fixNotification()方法:
private Notification fixNotification(Notification notification) {
.....................
if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
if (notification.getSmallIcon() == null) {
throw new IllegalArgumentException("Invalid notification (no valid small icon): "
+ notification);
}
}
...............
}
需要注意一下:当应用targetSdkVersion大于22时,在创建Notification时需要传入smallIcon,否则会抛异常导致发送不成功;接下来看一下enqueueNotificationWithTag()方法:
2.NotificationManagerService
NotificationManagerService继承了SystemService,在SystemServer中会进行启动,在start()方法内部会执行publishBinderService来进行Binder注册提供服务:
publishBinderService(Context.NOTIFICATION_SERVICE, mService,
/* allowIsolated= */ false,DUMP_FLAG_PRIORITY_CRITICAL DUMP_FLAG_PRIORITY_NORMAL);
2.1.enqueueNotificationWithTag()
final IBinder mService = new INotificationManager.Stub() {
....................................
@Override
public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Notification notification, int userId) throws RemoteException {
enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Binder.getCallingPid(), tag, id, notification, userId);
}
}
2.2.enqueueNotificationInternal()
可以看到,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 incomingUserId, boolean postSilently) {
..........................
//check
..........................
// Fix the notification as best we can.
try {
fixNotification(notification, pkg, tag, id, userId);
}
final StatusBarNotification n = new StatusBarNotification(
pkg, opPkg, id, tag, notificationUid, callingPid, notification,
user, null, mSystemClock.currentTimeMillis());
// setup local book-keeping
String channelId = notification.getChannelId();
if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
channelId = (new Notification.TvExtender(notification)).getChannelId();
}
String shortcutId = n.getShortcutId();
final NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel(
pkg, notificationUid, channelId, shortcutId,
true /* parent ok */, false /* includeDeleted */);
if (channel == null) {
.....................................
return;
}
final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
...........................
..........................
// Need escalated privileges to get package importance
final long token = Binder.clearCallingIdentity();
boolean isAppForeground;
try {
isAppForeground = mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
} finally {
Binder.restoreCallingIdentity(token);
}
mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground));
}
该方法中主要做了以下几件事:
1.进行各种check来确保notification的合法性;
2.将Notification作为参数创建StatusBarNotification;
3.获取Notification对应的channel id,根据channel id 来获取应用对应的NotificationChannel,如果为空的话,就直接返回了,因此应用在发送notification前需要先创建对应NotificationChannel;
4.通过Handler post EnqueueNotificationRunnable来执行后续逻辑;
2.3.EnqueueNotificationRunnable
protected class EnqueueNotificationRunnable implements Runnable {
.......................
@Override
public void run() {
........................
mEnqueuedNotifications.add(r);
final StatusBarNotification n = r.getSbn();
NotificationRecord old = mNotificationsByKey.get(n.getKey());
..........................
postPostNotificationRunnableMaybeDelayedLocked(
r, new PostNotificationRunnable(r.getKey()));
}
}
在EnqueueNotificationRunnable内部会将r(NotificationRecord)加入mEnqueuedNotifications进行管理,然后判断该NotificationRecord是否已经存在过,最后执行PostNotificationRunnable;
2.4.PostNotificationRunnable
protected class PostNotificationRunnable implements Runnable {
@Override
public void run() {
synchronized (mNotificationLock) {
try {
NotificationRecord r = null;
int N = mEnqueuedNotifications.size();
for (int i = 0; i < N; i++) {
final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
if (Objects.equals(key, enqueued.getKey())) {
r = enqueued;
break;
}
}
.......................
NotificationRecord old = mNotificationsByKey.get(key);
final StatusBarNotification n = r.getSbn();
final Notification notification = n.getNotification();
......................
int index = indexOfNotificationLocked(n.getKey());
if (index < 0) {
mNotificationList.add(r);
mUsageStats.registerPostedByApp(r);
r.setInterruptive(isVisuallyInterruptive(null, r));
} else {
old = mNotificationList.get(index); // Potentially *changes* old
mNotificationList.set(index, r);
mUsageStats.registerUpdatedByApp(r, old);
// Make sure we don't lose the foreground service state.
notification.flags |=
old.getNotification().flags & FLAG_FOREGROUND_SERVICE;
r.isUpdate = true;
final boolean isInterruptive = isVisuallyInterruptive(old, r);
r.setTextChanged(isInterruptive);
r.setInterruptive(isInterruptive);
}
mNotificationsByKey.put(n.getKey(), r);
...........................
if (notification.getSmallIcon() != null) {
StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
mListeners.notifyPostedLocked(r, old);
.........................
} finally {
int N = mEnqueuedNotifications.size();
NotificationRecord enqueued = null;
for (int i = 0; i < N; i++) {
enqueued = mEnqueuedNotifications.get(i);
if (Objects.equals(key, enqueued.getKey())) {
mEnqueuedNotifications.remove(i);
break;
}
}
// If the enqueued notification record had a cancel attached after it, execute
// it right now
if (enqueued != null && mDelayedCancelations.get(enqueued) != null) {
for (CancelNotificationRunnable r : mDelayedCancelations.get(enqueued)) {
r.doNotificationCancelLocked();
}
mDelayedCancelations.remove(enqueued);
}
}
}
}
}
PostNotificationRunnable的run()中主要处理逻辑如下:
1.从mEnqueuedNotifications中找到跟key对应的NotificationRecord;
2.通过indexOfNotificationLocked()看mNotificationList里面是否已经包含该NotificationRecord,如果不存在,说明是新的record,需要加入mNotificationList进行管理;否则的话,将mNotificationList中index对应的NotificationRecord进行更新;
3.将要处理的NotificationRecord放入mNotificationsByKey进行管理;
4.执行mListeners.notifyPostedLocked(r, old)来进行通知;
5.在finally里面将要处理的NotificationRecord从mEnqueuedNotifications里面移除;
6.如果在加入后有取消操作,需要立刻执行取消操作,并将NotificationRecord从mDelayedCancelations中移除;
四.显示
前面讲到,在PostNotificationRunnable中会执行mListeners.notifyPostedLocked(r, old)进行通知,mListeners是NotificationListeners实例:
1.NotificationListeners
public void notifyPostedLocked(NotificationRecord r, NotificationRecord old) {
notifyPostedLocked(r, old, true);
}
跟随调用关系:
private void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
boolean notifyAllListeners) {
try {
// Lazily initialized snapshots of the notification.
StatusBarNotification sbn = r.getSbn();
StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
TrimCache trimCache = new TrimCache(sbn);
for (final ManagedServiceInfo info : getServices()) {
..............................
final StatusBarNotification sbnToPost = trimCache.ForListener(info);
mHandler.post(() -> notifyPosted(info, sbnToPost, update));
}
}
}
通过getServices()来找到已经注册过的ManagedServiceInfo列表,最后执行notifyPosted();
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);
}
}
在notifyPosted()内部通过info.service来找到对应的INotificationListener实例,对应NotificationListenerService内部的NotificationListenerWrapper,然后将StatusBarNotification封装成StatusBarNotificationHolder,最后执行NotificationListenerWrapper的onNotificationPosted()方法:
2.NotificationListenerWrapper
protected class NotificationListenerWrapper extends INotificationListener.Stub {
@Override
public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
NotificationRankingUpdate update) {
StatusBarNotification sbn;
try {
sbn = sbnHolder.get();
}
.................................
// protect subclass from concurrent modifications of (@link mNotificationKeys}.
synchronized (mLock) {
applyUpdateLocked(update);
if (sbn != null) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = sbn;
args.arg2 = mRankingMap;
mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED,
args).sendToTarget();
}
}
..............
}
最终会通过Handler来发送消息来进行处理;
3.NotificationListenerService
case MSG_ON_NOTIFICATION_POSTED: {
SomeArgs args = (SomeArgs) msg.obj;
StatusBarNotification sbn = (StatusBarNotification) args.arg1;
RankingMap rankingMap = (RankingMap) args.arg2;
args.recycle();
onNotificationPosted(sbn, rankingMap);
}
break;
onNotificationPosted(sbn, rankingMap)是在CarNotificationListener内部实现的;
4.CarNotificationListener
@Override
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
.....................
AlertEntry alertEntry = new AlertEntry(sbn);
onNotificationRankingUpdate(rankingMap);
notifyNotificationPosted(alertEntry);
}
在CarNotificationListener内部会将StatusBarNotification封装成AlertEntry,然后执行notifyNotificationPosted():
private void notifyNotificationPosted(AlertEntry alertEntry) {
..........................
postNewNotification(alertEntry);
}
一步一步调用:
private void postNewNotification(AlertEntry alertEntry) {
mActiveNotifications.put(alertEntry.getKey(), alertEntry);
sendNotificationEventToHandler(alertEntry, NOTIFY_NOTIFICATION_POSTED);
}
先将alertEntry存入mActiveNotifications进行管理;然后执行sendNotificationEventToHandler发送NOTIFY_NOTIFICATION_POSTED消息;
private void sendNotificationEventToHandler(AlertEntry alertEntry, int eventType) {
Message msg = Message.obtain(mHandler);
msg.what = eventType;
msg.obj = alertEntry;
mHandler.sendMessage(msg);
}
该Handler是通过setHandler来赋值的,具体是在什么地方呢?
这个需要回到最前面NotificationPanelViewController里面了,前面说到NotificationViewController是在显示环节进行分析,轮到NotificationViewController登场了;
5.NotificationViewController
NotificationViewController是在NotificationPanelViewController实例化,并执行enable()方法,先看一下构造方法:
public NotificationViewController(CarNotificationView carNotificationView,
PreprocessingManager preprocessingManager,
CarNotificationListener carNotificationListener,
CarUxRestrictionManagerWrapper uxResitrictionListener,
NotificationDataManager notificationDataManager) {
mCarNotificationView = carNotificationView;
mPreprocessingManager = preprocessingManager;
mCarNotificationListener = carNotificationListener;
mUxResitrictionListener = uxResitrictionListener;
mNotificationDataManager = notificationDataManager;
}
在构造方法内部,会传入CarNotificationView、CarNotificationListener、PreprocessingManager等实例,都是跟显示有关的核心类;
1.CarNotificationView:负责处理通知显示;
2.PreprocessingManager:负责管理通知,通过CarNotificationListener来获取通知;
3.CarNotificationListener:跟NotificationManagerService间接交互的类;
public void enable() {
mCarNotificationListener.setHandler(mNotificationUpdateHandler);
..................
}
private class NotificationUpdateHandler extends Handler {
@Override
public void handleMessage(Message message) {
if (mIsVisible) {
updateNotifications(
mShowLessImportantNotifications,message.what,(AlertEntry) message.obj);
} else {
resetNotifications(mShowLessImportantNotifications);
}
}
}
可以看到Handler是在NotificationViewController里面实现的,当有消息到来时,如果CarNotificationView显示时执行updateNotifications()来直接显示通知;不显示时执行resetNotifications()来对通知进行管理;
private void updateNotifications(
boolean showLessImportantNotifications, int what, AlertEntry alertEntry) {
if (mPreprocessingManager.shouldFilter(alertEntry,
mCarNotificationListener.getCurrentRanking())) {
// if the new notification should be filtered out, return early
return;
}
mCarNotificationView.setNotifications(
mPreprocessingManager.updateNotifications(
showLessImportantNotifications,
alertEntry,
what,
mCarNotificationListener.getCurrentRanking()));
}
以上就是Notification的整个工作过程,最后用一张流程图来总结一下: