> 文档中心 > 深入理解通知服务NotificationListenerService原理

深入理解通知服务NotificationListenerService原理


前言

在上一篇通知服务NotificationListenerService使用方法 中,我们已经介绍了如何使用NotificationListenerService来监听消息通知,在最后我们还模拟了如何实现微信自动抢红包功能。

那么NotificationListenerService是如何实现系统通知监听的呢?(本篇源码分析基于API 32)

NotificationListenerService方法集

NotificationLisenerService是Service的子类

public abstract class NotificationListenerService extends Service

除了Service的方法属性外,NotificationListenerService还为我们提供了收到通知、通知被移除、连接到通知管理器等方法,如下图所示

一般业务中我们只关注有标签的那四个方法即可。

NotificationListenerService接收流程

既然NotificationListenerService是继承自Service的,我们先来看它的onBind方法,代码如下所示。

@Overridepublic IBinder onBind(Intent intent) {    if (mWrapper == null) {        mWrapper = new NotificationListenerWrapper();    }    return mWrapper;}

在onBind方法中返回了一个NotificationListenerWrapper实例,NotificationListenerWrapper对象是定义在NotificationListenerService中的一个内部类。主要方法如下所示。

/** @hide */protected class NotificationListenerWrapper extends INotificationListener.Stub {    @Override    public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,            NotificationRankingUpdate update) {        StatusBarNotification sbn;        try {            sbn = sbnHolder.get();        } catch (RemoteException e) {            Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification", e);            return;        }        if (sbn == null) {            Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification");            return;        }        try {            // convert icon metadata to legacy format for older clients            createLegacyIconExtras(sbn.getNotification());            maybePopulateRemoteViews(sbn.getNotification());            maybePopulatePeople(sbn.getNotification());        } catch (IllegalArgumentException e) {            // warn and drop corrupt notification            Log.w(TAG, "onNotificationPosted: can't rebuild notification from " +                    sbn.getPackageName());            sbn = null;        }        // 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();            } else {                // still pass along the ranking map, it may contain other information                mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE,                        mRankingMap).sendToTarget();            }        }        ...省略onNotificationRemoved等方法    }

 NotificationListenerWrapper继承自INotificationListener.Stub,当我们看到Stub这一关键字的时候,就应该知道这里是使用AIDL实现了跨进程通信。

在NotificationListenerWrapper的onNotificationPosted中通过代码

mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED,                        args).sendToTarget();

将消息发送出去,handler接受后,又调用NotificationListernerService的onNotificationPosted方法,进而实现通知消息的监听。代码如下所示。

private final class MyHandler extends Handler {        public static final int MSG_ON_NOTIFICATION_POSTED = 1;        @Override        public void handleMessage(Message msg) {            if (!isConnected) {                return;            }            switch (msg.what) {                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;           ...            }        }    }

 那么,消息通知发送时,又是如何与NotificationListenerWrapper通信的呢?

通知消息发送流程

当客户端发送一个通知的时候,会调用如下所示的代码

notificationManager.notify(1, notification)

notify又会调用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();    }}

 紧接着又会走到INotificationManager的enqueueNotificationWithTag方法中,enqueueNotificationWithTag是声明在INotificationManager.aidl文件中的接口

/** {@hide} */interface INotificationManager{    @UnsupportedAppUsage    void cancelAllNotifications(String pkg, int userId);    ...    void cancelToast(String pkg, IBinder token);    void finishToken(String pkg, IBinder token);    void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,            in Notification notification, int userId);    ... }

这个接口是在NotificationManagerService中实现的,接着我们转到NotificationManagerService中去查看,相关主要代码如下所示。

@VisibleForTestingfinal 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);         }}

enqueueNotificationWithTag方法会走进enqueueNotificationInternal方法,在方法最后会通过Handler发送一个EnqueueNotificationRunnable,代码如下所示。

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) {        ...        //构造StatusBarNotification,用于分发监听服务        final StatusBarNotification n = new StatusBarNotification(                pkg, opPkg, id, tag, notificationUid, callingPid, notification,                user, null, System.currentTimeMillis());        // setup local book-keeping        String channelId = notification.getChannelId();        if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {            channelId = (new Notification.TvExtender(notification)).getChannelId();        }        ...        // 设置intent的白名点,是否盛典、是否后台启动等        if (notification.allPendingIntents != null) {            final int intentCount = notification.allPendingIntents.size();            if (intentCount > 0) {                final long duration = LocalServices.getService(                        DeviceIdleInternal.class).getNotificationAllowlistDuration();                for (int i = 0; i < intentCount; i++) {                    PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);                    if (pendingIntent != null) {                        mAmi.setPendingIntentAllowlistDuration(pendingIntent.getTarget(),                                ALLOWLIST_TOKEN, duration,                                TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,                                REASON_NOTIFICATION_SERVICE,                                "NotificationManagerService");                        mAmi.setPendingIntentAllowBgActivityStarts(pendingIntent.getTarget(),                                ALLOWLIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER                                        | FLAG_SERVICE_SENDER));                    }                }            }        }        ...        mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground));    }

EnqueueNotificationRunnable源码如下所示。

protected class EnqueueNotificationRunnable implements Runnable {        private final NotificationRecord r;        private final int userId;        private final boolean isAppForeground;        EnqueueNotificationRunnable(int userId, NotificationRecord r, boolean foreground) {            this.userId = userId;            this.r = r;            this.isAppForeground = foreground;        }        @Override        public void run() {            synchronized (mNotificationLock) {                ...                //将通知加入队列                mEnqueuedNotifications.add(r);                scheduleTimeoutLocked(r);                ...                if (mAssistants.isEnabled()) {                    mAssistants.onNotificationEnqueuedLocked(r);                    mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),                            DELAY_FOR_ASSISTANT_TIME);                } else {                    mHandler.post(new PostNotificationRunnable(r.getKey()));                }            }        }    }

在EnqueueNotificationRunnable最后又会发送一个PostNotificationRunable,

PostNotificationRunable源码如下所示。

protected class PostNotificationRunnable implements Runnable {        private final String key;        PostNotificationRunnable(String key) {            this.key = key;        }        @Override        public void run() {            synchronized (mNotificationLock) {                try {                    ...                    //发送通知                    if (notification.getSmallIcon() != null) {                        StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;                        mListeners.notifyPostedLocked(r, old);                        if ((oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup()))                                && !isCritical(r)) {                            mHandler.post(new Runnable() {                                @Override                                public void run() {                                    mGroupHelper.onNotificationPosted(                                            n, hasAutoGroupSummaryLocked(n));                                }                            });                        } else if (oldSbn != null) {                            final NotificationRecord finalRecord = r;                            mHandler.post(() -> mGroupHelper.onNotificationUpdated(                                    finalRecord.getSbn(), hasAutoGroupSummaryLocked(n)));                        }                    } else {                        //...                    }                } finally {                    ...                }            }        }    }

从代码中可以看出,PostNotificationRunable类中会调用notifyPostedLocked方法,这里你可能会有疑问:这里分明判断notification.getSmallIcon()是否为null,不为null时才会进入notifyPostedLocked方法。为什么这里直接默认了呢?这是因为在Android5.0中规定smallIcon不可为null,且NotificationListenerService仅适用于5.0以上,所以这里是必然会执行到notifyPostedLocked方法的。

其方法源码如下所示。

 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);                //循环通知每个ManagedServiceInfo对象                for (final ManagedServiceInfo info : getServices()) {                    ...                    mHandler.post(() -> notifyPosted(info, sbnToPost, update));                }            } catch (Exception e) {                Slog.e(TAG, "Could not notify listeners for " + r.getKey(), e);            }        }

notifyPostedLocked方法最终会调用notifyPosted方法,我们再来看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);           } catch (RemoteException ex) {                Slog.e(TAG, "unable to notify listener (posted): " + info, ex);           } }

 notifyPosted方法,最终会调用INotificationListerner的onNotificationPosted方法,这样就通知到了NotificationListenerService的onNotificationPosted方法。

上述方法的流程图如下图所示。

NotificationListenerService注册

在NotificationListenerService中通过registerAsSystemService方法注册服务,代码如下所示。

 @SystemApi    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);    }

registerAsSystemService方法将NotificationListenerWrapper对象注册到NotificationManagerService中。如此就实现了对系统通知的监听。

总结

NotificationListenerService实现对系统通知的监听可以概括为三步:

  • NotificationListenerService将 NotificationListenerWrapper注册到NotificationManagerService中。

  • 当有通知被发送时 ,NotificationManagerService跨进程通知到每个NotificationListenerWrapper。

  • NotificationListenerWrapper中信息由NotificationListenerService类中的Handler中处理,从而调用NotificationListenerService中对应的回调方法。

央视天气网