前语

在上一篇告诉服务NotificationListenerService运用办法 中,咱们已经介绍了怎么运用NotificationListenerService来监听音讯告诉,在终究咱们还模拟了怎么完成微信主动抢红包功用。

那么NotificationListenerService是怎么完成体系告诉监听的呢?(本篇源码分析基于API 32)

NotificationListenerService办法集

NotificationLisenerService是Service的子类

public abstract class NotificationListenerService extends Service

除了Service的办法特点外,NotificationListenerService还为咱们供给了收到告诉、告诉被移除、连接到告诉管理器等办法,如下图所示。

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

NotificationListenerService接纳流程

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

@Override
public 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中去查看,相关首要代码如下所示。

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

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注册

在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中对应的回调办法。