软件编程
位置:首页>> 软件编程>> Android编程>> Android NotificationListenerService 通知服务原理解析

Android NotificationListenerService 通知服务原理解析

作者:黄林晴  发布时间:2022-03-29 05:34:29 

标签:Android,NotificationListenerService,通知服务

前言

在上一篇通知服务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方法。

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

Android 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将 NotificationListenerWrapper注册到NotificationManagerService中。

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

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

来源:https://juejin.cn/post/7166864980632371207

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com