preface

As android users, we have not always felt that android mobile phone notification bar ≈ garbage dump, in addition to a few useful social communication notifications, the other are some junk push notifications, many people will choose to directly shut down, some people ignore.

While native Android and third-party Android systems have been optimized for notification management in recent years, they only provide users with more choices, important or secondary, priority or silent, and are still a bit clunky and difficult to use.

As a result, there are notification management apps on the market that filter out unwanted notifications more accurately, make the notification bar cleaner, and provide useful and interesting features based on system notifications. Let’s take a look at how these functions are implemented.

How to listen for notifications

In fact, the Android system provides the ability to implement notification listening. Android4.3 joined the notification service NotificationListenerService monitoring, we developed the APP with this permission can monitor the current system after the change of the notification, Android4.4 can also get notification information for details. Let’s take a look at the specific use.

Realize three steps of listening

1. Create a class, inheritance NotificationListenerService

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public class NotificationListener extends NotificationListenerService {
  
  // When the system receives a new notification, the callback starts
  @Override
  public void onNotificationPosted(StatusBarNotification sbn) {
    super.onNotificationPosted(sbn);
  }
	
  // Start the callback when the system notification is deleted
  @Override
  public void onNotificationRemoved(StatusBarNotification sbn) {
    super.onNotificationRemoved(sbn); }}Copy the code

2. Register Service in androidmanifest.xml and declare related permissions

 <service
      android:name=".NotificationListener"
      android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
      <intent-filter>
        <action android:name="android.service.notification.NotificationListenerService" />
      </intent-filter>
    </service>
Copy the code

3. On the Notice Right page, select APP

After the above two steps are completed, after the app is installed on the mobile phone, you still need to check the app on the page of “Notification Right”. This path is “Settings -> Privacy -> Notification right” on the native system, but on the third-party system, the path is inconsistent and difficult to find. You can skip to it by following the code.

private void jumpNotificationAccessSetting(Context context) {
    try {
      Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      context.startActivity(intent);
    } catch(Exception e) { e.printStackTrace(); }}Copy the code

After completing the first three steps, we can listen to the notifications sent by the system in our own app. NotificationListenerService related interfaces and StatusBarNotification and Notification of some important fields, you can refer to appendix 1.

Notification tricks

Through the analysis in the above section, we know how to get notifications sent by other apps and some methods of controlling notifications provided by the system. What kind of tricks can we play with notifications based on these capabilities? At present, there are some apps on the market, such as notification filtering, notification voice broadcasting, grabbing red packets, wechat message withdrawal prevention, etc. Let’s analyze how these functions are implemented.

1. Notification filtering

Notification filtering automatically removes notifications that meet certain conditions, such as those that contain certain keywords, those that are sent by certain apps, those that are sent in a certain period of time, and those that are sent in specific mobile phone states (off/on screen, charging/battery).

The following figure shows the notification containing “headline” in iQiyi in the app of “Notification Filter Box”. Its filtering conditions can be a combination of different text contents, including or not including.



The following figure shows the notification of automatic filtering of advertising and marketing and the notification of automatic receiving of content information in yizhi App.


Processing flow

Through NotificationListenerService# onNotificationPosted () interface to get the notice after instance, we extract inform key information such as the content, the package name, delivery time, then put these information into the a series of filters, CancelNotification removal or snoozeNotification freeze is called for notifications that meet the filter criteria.

Technical point

1. Obtain the text of the standard notification

Notifications sent by most apps today are standard notifications that are easy to retrieve in text.

@Override
public void getContent(StatusBarNotification sbn) {
    Notification notification = sbn.getNotification();
    Bundle extras = notification.extras;
    if(extras ! =null) {
        // Get the notification title
        String title = extras.getString(Notification.EXTRA_TITLE, "");
        // Get the notification content
        String content = extras.getString(Notification.EXTRA_TEXT, ""); }}Copy the code

2. Obtain the text content of the non-standard notification

A non-standard notification is a notification that is implemented by setCustomContentView or setCustomBigContentView methods. Text information cannot be obtained by conventional content retrieval methods, so we can get it by traversing the View methods.

The main process has the following three steps:

  1. Get nonstandard notification contentView and bigContentView;
  2. Call RemoteViews#apply to convert RemoteViews to View;
  3. Walk through the View and get the text content of the TextView.
  // Get the view for notification
  public static View getContentView(Context context, Notification notification) {
    RemoteViews contentView = null;
    / / get the contentView
    if(notification.contentView ! =null) {
      contentView = notification.contentView;
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
      contentView = Notification.Builder.recoverBuilder(context, notification).createContentView();
    }

    / / RemoteViews into view
    View view = null;
    try {
      view = contentView == null ? null : contentView.apply(context, null);
    } catch (Throwable e) {
    }
    return view;
  }


  // Get the text inside the view
  public static String getContent(View view) {
    StringBuilder stringBuilder = new StringBuilder();
    traversalView(view, stringBuilder);
    return stringBuilder.toString();
  }

  // Walk through the View to get the text content of the TextView
  private static void traversalView(View view, StringBuilder stringBuilder) {
    if (view instanceof ViewGroup) {
      ViewGroup viewGroup = (ViewGroup) view;
      int count = viewGroup.getChildCount();

      for (int i = 0; i < count; ++i) { View childView = viewGroup.getChildAt(i); traversalView(childView, stringBuilder); }}else {
      if (view instanceof TextView) {
        TextView tv = (TextView) view;
        CharSequence text = tv.getText();
        stringBuilder.append(text);
        stringBuilder.append(";"); }}}Copy the code

3. Remove resident notifications

Resident notifications are notifications with Notification#isClearable() as false, which cannot be cleared even after the “clear all” button is clicked. The user can only clear them manually by turning off the application notification or the corresponding notification delete button.

The most common notifications are those for music apps, but more of them are for the purpose of adding entry to the app or keeping the process alive. These are the kind of resident notifications that we actually want to remove most.

After Android8.0, the system opens interface snoozeNotification(String Key, Long durationMs), which is actually the method of freezing notification. Key is the only mark of notification, durationMs is freezing duration, generally set a large value, The purpose of clearing resident notifications can be realized, and the freezing operation will become invalid after the phone restarts.

2. Anti-withdrawal of wechat messages

The message withdrawal function on wechat can be said to make people both love and hate. Love is that the wrong message can be withdrawn in time, and hate is that others can also be withdrawn. Every time you see your friend withdraw a message, you must wonder what he/she has withdrawn.

Want to achieve wechat message withdrawal itself is very difficult, before is through the root xposed framework, and then hook wechat internal methods to achieve, now through the notice monitoring service can also be achieved.

The following figure shows the anti-withdrawal function interface of wechat implemented by a third-party app. In fact, it lists all the messages that may be withdrawn for you, and you can identify which one is the withdrawn message by yourself.

Implementation scheme

The prerequisite for realizing this function is that the notification permission of wechat is turned on. WeChat when we received the news, to extract the key and the information such as name, timestamp, deposited in the database, when users click withdraw message query messages within 2 minutes record (withdraw valid time 2 minutes), then the user according to this list to judge which one is withdrawn, the inability to locate precisely the withdrawal news). The general process is as follows:

3. Notification enhancement

Notice enhancement is mainly to extend some additional capabilities to the notice, such as suspension notice, bullet-screen notice, voice broadcast, custom ringtone, vibration, automatic jump, etc. You can think about the scenarios in which these functions can be used.

Bullet-screen notification: it is convenient to view notifications in full screen state such as playing games and watching videos;

Custom ringtones: Set different ringtones for different friends and groups of IM apps such as wechat, QQ and Soul.

Voice broadcast: not missing important notices when driving or walking;

Below is the notification extension capability provided by some third-party apps.

Floating notification Custom ringtone voice broadcast



Technical solution

Let’s take a look at how these functions work. The first step is to define matching rules, such as keyword inclusion or regular expression matching, followed by the corresponding execution actions of the rules, which ultimately invoke the capabilities provided by the system.

4. Grab a red envelope

WeChat grab a red envelope, through NotificationListenerService listening to a red envelope, the first step is to inform, then jump to a red envelope page, finally through the auxiliary services AccessibilityService to simulate click action, grab a red envelope functions. The technical difficulties are mainly the judgment of red envelope notification and the realization of simulated click, which involves auxiliary service related technologies and will not be discussed here.

Red envelope notification judgment

It is mainly to judge whether the notification content contains “[wechat red envelope]”.

 public void processRedPacketNotification(StatusBarNotification sbn) {
    PendingIntent pendingIntent = null;
    Notification notification = sbn.getNotification();
    Bundle extras = notification.extras;
    // Get the notification content
    String content = extras.getString(Notification.EXTRA_TEXT, "");
    if(! TextUtils.isEmpty(content) && content.contains([wechat Red envelope])) {
      pendingIntent = notification.contentIntent;
    }

    // Send pendingIntent to open wechat
    try {
      if(pendingIntent ! =null) { pendingIntent.send(); }}catch(PendingIntent.CanceledException e) { e.printStackTrace(); }}Copy the code

Analyze the source code

Since all talk so much, we want to analyze the notification send source code. Notification module involves the main class has a Notification, NotificationChannel, NotificationManager, NotificationManagerService, NotificationListenerService, etc , the relationship between these classes is as follows:

  1. NotificationManagerServiceIs the core of the whole module, it will be started and run in the background when the system is started, responsible for all notices in the system receiving and receiving, processing, display, removal and other logic;
  2. NotificaionManagerIs to notify the management class, responsible for sending notice, notice, etc., by way of Binder call NotificationManagerService;
  3. NotificationIs the entity class of the notification, which defines the notification title, content, icon, jump information, etc.

Call process from application, through the NotificationManager. Notify () issued a circular, the NotificationManager send notification to the NotificationManagerService by binder mechanism, Distributed to NotificationListeners NotificationManagerService put notice set in the listener, including SystemUI, desktop, sports health app and other third party registration in the app.

Notification process (based on Android10 source) as follows, from notifications – > NotificationManagerService processing – > distribution to inform monitoring service.

Step analysis

The following analysis will be broken down step by step based on the above flow chart.

1. NotificationChannel and Notification

    public void sendNotification(View view) {
        String id = "channel_1";
        String des = "des_1";
        NotificationChannel channel = new NotificationChannel(id, des, NotificationManager.IMPORTANCE_MIN);
        notificationManager.createNotificationChannel(channel);
        Notification notification = new Notification.Builder(MainActivity.this, id)
                .setContentTitle("Hello")
                .setContentText("You have a new message.")
                .setSmallIcon(R.drawable.icon)
                .setStyle(new Notification.MediaStyle())
                .setAutoCancel(false)
                .build();
        notificationManager.notify(1, notification);
    }
Copy the code

Use NotificationManager to create a new Notification channel or get an already created Notification channel, and then use Builder to create a Notification object. Finally, the Notification is sent through NotificationManager#notify().

2, NotificationManager# notifyAsUser

    public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
    {
      	// Get system notification service, called with Binder
        INotificationManager service = getService();
        String pkg = mContext.getPackageName();
        try {
          // Do some sound, icon, contentView optimization and validation work.
            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,fixNotification(notification), user.getIdentifier());
        } catch (RemoteException e) {
            throwe.rethrowFromSystemServer(); }}Copy the code

In notifyAsUser, fixNotification is called to preprocess notifications, such as Notification small icon processing, image resource clipping, low memory compatibility, etc. Then direct call NotificationManagerService# enqueueNotificationWithTag (), here by binder calls into system_server process.

3, NotificationManagerService# 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) {
        // Check whether the calling uid has permission to send messages
        final int notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
				
    	// Check the validity of the classification
        checkRestrictedCategories(notification);

        // Continue to fix notifications, mainly for fullScreenIntent handlingfixNotification(notification, pkg, userId); .// Get the notification channel information and verify whether it is normal
        final NotificationChannel channel = mPreferencesHelper.getNotificationChannel(pkg,
                notificationUid, channelId, false /* includeDeleted */);
        if (channel == null) {
            boolean appNotificationsOff = mPreferencesHelper.getImportance(pkg, notificationUid)
                    == NotificationManager.IMPORTANCE_NONE;

            if(! appNotificationsOff) { doChannelWarningToast("Developer warning for package "" + pkg + ""\n" +
                        "Failed to post notification on channel "" + channelId + ""\n" +
                        "See log for more details");
            }
            return;
        }

    	// Construct the StatusBarNotification object to be distributed to listener services, including SystemUI, etc
        final StatusBarNotification n = new StatusBarNotification(
                pkg, opPkg, id, tag, notificationUid, callingPid, notification,
                user, null, System.currentTimeMillis());
    
    	// Construct a NotificationRecord object, which is used at the framework level
        final NotificationRecord r = newNotificationRecord(getContext(), n, channel); .// Check the rate at which the application sends notifications and the total number of notifications (maximum 25 for a single application) to determine whether the application can send notifications
        if(! checkDisqualifyingFeatures(userId, notificationUid, id, tag, r, r.sbn.getOverrideGroupKey() ! =null)) {
            return;
        }

        // Whitelist pendingIntents, such as power saving mode, background start activity, etc.
        if(notification.allPendingIntents ! =null) {
            final int intentCount = notification.allPendingIntents.size();
            if (intentCount > 0) {
                final ActivityManagerInternal am = LocalServices
                        .getService(ActivityManagerInternal.class);
                final long duration = LocalServices.getService(
                        DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
                for (int i = 0; i < intentCount; i++) {
                    PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
                    if(pendingIntent ! =null) {
                        am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
                                WHITELIST_TOKEN, duration);
                        am.setPendingIntentAllowBgActivityStarts(pendingIntent.getTarget(),
                                WHITELIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER
                                        | FLAG_SERVICE_SENDER));
                    }
                }
            }
        }

        mHandler.post(new EnqueueNotificationRunnable(userId, r));
    }
Copy the code

The code here runs in the system_server process and has a few key points:

  1. Get notification channel information. This is not set after Android 8.0channelThe notification cannot be sent;
  2. Except for system notifications and applications with registered listeners, notifications of other apps will limit the maximum number of notifications and the maximum frequency of notifications.
  3. Add PendingIntent of Notification to whitelist, such as power saving mode, background start activity, etc.
  4. Notification is further encapsulated asStatusBarNotificationNotificationRecord, and finally encapsulated into an asynchronous threadEnqueueNotificationRunnableIn the.
  5. StatusBarNotificationThe mainClient-facing, contains only the information that the user needs to know, such as the notification package name, ID, key, etc., and is finally called back to the listener.NotificationRecordThe mainFrame-oriented layer, in addition to holdingStatusBarNotificationIn fact, it also encapsulates various notification related information, such asChannel, sound, vibrationWait, this information is needed when the server processes notifications.

4, EnqueueNotificationRunnable

protected class EnqueueNotificationRunnable implements Runnable {
        private final NotificationRecord r;
        private final int userId;

        EnqueueNotificationRunnable(int userId, NotificationRecord r) {
            this.userId = userId;
            this.r = r;
        };

        @Override
        public void run(a) {
            synchronized (mNotificationLock) {
              	ArrayList
      
        mEnqueuedNotifications = new ArrayList<>()
      
                mEnqueuedNotifications.add(r);
                 // Cancel the timer
                scheduleTimeoutLocked(r);

                final StatusBarNotification n = r.sbn;
               
              // Find a NotificationRecord by key, keeping the same order if there is one
                NotificationRecord old = mNotificationsByKey.get(n.getKey());
                if(old ! =null) {
                    // Copy the sort informationr.copyRankingInformation(old); }...// Process NotificationGroup informationhandleGroupedNotificationLocked(r, old, callingUid, callingPid); .if (mAssistants.isEnabled()) {
                    // NotificationAssistants handle notifications
                    mAssistants.onNotificationEnqueued(r);
                    mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
                            DELAY_FOR_ASSISTANT_TIME);
                } else {
                    mHandler.post(newPostNotificationRunnable(r.getKey())); }}}}Copy the code

Main concerns:

  1. Add NotificationRecord to the queue;
  2. Timing cancel function, construction of notification can be passedsetTimeoutMethod setting;
  3. Update NotificationGroup information;
  4. MHandler isWorkerHandlerClass, in theNotificationManagerService#onStartMethod, soEnqueueNotificationRunnableThe run method of system_server will run on the main thread of system_server.

5, PostNotificationRunnable

protected class PostNotificationRunnable implements Runnable {
        private final String key;

        PostNotificationRunnable(String key) {
            this.key = key;
        }

        @Override
        public void run(a) {
            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;
                        }
                    }

                    r.setHidden(isPackageSuspendedLocked(r));
                    NotificationRecord old = mNotificationsByKey.get(key);
                    final StatusBarNotification n = r.sbn;
                    final Notification notification = n.getNotification();
                 
                  // Find the Record by key, there is an update, there is no new
                    int index = indexOfNotificationLocked(n.getKey());
                    if (index < 0) {
                        mNotificationList.add(r);
                        mUsageStats.registerPostedByApp(r);
                        r.setInterruptive(isVisuallyInterruptive(null, r));
                    } else {
                        old = mNotificationList.get(index);
                        mNotificationList.set(index, r);
                        mUsageStats.registerUpdatedByApp(r, old);
                        // Make sure there are no lost flags for foreground services
                        notification.flags |=
                                old.getNotification().flags & FLAG_FOREGROUND_SERVICE;
                        r.isUpdate = true;
                        r.setTextChanged(isVisuallyInterruptive(old, r));
                    }

                    mNotificationsByKey.put(n.getKey(), r);

                    // The foreground service is set to flag
                    if((notification.flags & FLAG_FOREGROUND_SERVICE) ! =0) {
                        notification.flags |= Notification.FLAG_ONGOING_EVENT
                                | Notification.FLAG_NO_CLEAR;
                    }

                    applyZenModeLocked(r);
                   // Sort mNotificationList
                    mRankingHelper.sort(mNotificationList);
										
                    if(notification.getSmallIcon() ! =null) {
                      // Send notificationsStatusBarNotification oldSbn = (old ! =null)? old.sbn :null;
                        mListeners.notifyPostedLocked(r, old);
                        if (oldSbn == null| |! Objects.equals(oldSbn.getGroup(), n.getGroup())) {// Send group notification
                            mHandler.post(new Runnable() {
                                @Override
                                public void run(a) { mGroupHelper.onNotificationPosted( n, hasAutoGroupSummaryLocked(n)); }}); }}else {
                        // No small icon, remove notification
                        if(old ! =null && !old.isCanceled) {
                            mListeners.notifyRemovedLocked(r,
                                    NotificationListenerService.REASON_ERROR, null);
                            mHandler.post(new Runnable() {
                                @Override
                                public void run(a) { mGroupHelper.onNotificationRemoved(n); }}); }}if(! r.isHidden()) {// Handle ringing, vibration, etc
                        buzzBeepBlinkLocked(r);
                    }
                    maybeRecordInterruptionLocked(r);
                } finally {
                  // Finally remove the NotificationRecord from the queue
                    int N = mEnqueuedNotifications.size();
                    for (int i = 0; i < N; i++) {
                        final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
                        if (Objects.equals(key, enqueued.getKey())) {
                            mEnqueuedNotifications.remove(i);
                            break;
                        }
                    }
                }
            }
        }
    }
Copy the code

Mainly for the

  1. Determine whether notifications are new or refreshed;
  2. The refresh NotificationGroup;
  3. Ringing and vibration processing;
  4. Call NotificationListeners#notifyPostedLocked.

6, NotificationListeners

 private void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
                boolean notifyAllListeners) {
            // Lazily initialized snapshots of the notification.StatusBarNotification sbn = r.sbn; StatusBarNotification oldSbn = (old ! =null)? old.sbn :null;
            TrimCache trimCache = new TrimCache(sbn);

            for (final ManagedServiceInfo info : getServices()) {
                boolean sbnVisible = isVisibleToListener(sbn, info);
                booleanoldSbnVisible = oldSbn ! =null ? isVisibleToListener(oldSbn, info) : false; .final NotificationRankingUpdate update = makeRankingUpdateLocked(info);

                if(oldSbnVisible && ! sbnVisible) {final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
                    mHandler.post(new Runnable() {
                        @Override
                        public void run(a) {
                            // Old notification visible, new notification not visible, remove notification
                            notifyRemoved(
                                    info, oldSbnLightClone, update, null, REASON_USER_STOPPED); }});continue;
                }

                final StatusBarNotification sbnToPost = trimCache.ForListener(info);
              	// Send notifications
                mHandler.post(new Runnable() {
                    @Override
                    public void run(a) { notifyPosted(info, sbnToPost, update); }}); }}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) {
                Log.e(TAG, "unable to notify listener (posted): "+ listener, ex); }}Copy the code

The above process mainly sends notifications to each listener:

  1. Looking for a matchManagedServiceInfo, which encapsulates the registered notification listening service information;
  2. Notification listener removes Notification that is not displayed.
  3. Sending a new notification to the listener
  4. OnNotificationPosted takes an sbnHolder instead of an SBN object, and you can see that there’s a get() method that returns the actual SBN object. After app receives the sbnHolder, it needs to call binder again to obtain the SBN object. Why? SbnHolder:

Wrapper for a StatusBarNotification object that allows transfer across a oneway binder without sending large amounts of data over a oneway transaction.

The idea is to encapsulate the SBN object with a one way binder call to avoid transferring a lot of data.

7, NotificationListenerService

  public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder, NotificationRankingUpdate update) {
            StatusBarNotification sbn;
            try {
                // Retrieve the real SBN
                sbn = sbnHolder.get(); 
            } catch (RemoteException e) {
                Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification", e);
                return; }...synchronized (mLock) {
                applyUpdateLocked(update);
                if(sbn ! =null) {
                    SomeArgs args = SomeArgs.obtain();
                    args.arg1 = sbn;
                    args.arg2 = mRankingMap;
                    // This is handled by handler
           mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED,
                            args).sendToTarget();
                } else{ mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE, mRankingMap).sendToTarget(); }}}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;
Copy the code

Here to pass from system_server binder way back to the process of app, the sbnHolder instance came, sbnHolder is IStatusBarNotificationHolder Stub object. We then fetch the real instance of StatusBarNotification via sbnholder.get (), which we can tell from the exception type of RemoteException is a remote call. Once you get the StatusBarNotification instance, you use onNotificationPosted, which mHandler throws to subclasses, so that listeners can receive the new notification.

conclusion

The above analysis reveals the secrets behind Android notification tricks, Know how the third party app to monitor notice, cancellation notice and analyzed from the source level from app notifications – > NotificationManagerService processing notice – > notice distributed to NotificationListenerService the whole process of the client.

Of course, there are many more interesting ways to explore Android notification management!

Hi, I am xiaoqiang from Kuaishou electronic commerce ~ Kuaishou electronic commerce wireless technology team is looking for talents 🎉🎉🎉! We are the core business line of the company, which is full of talents, opportunities and challenges. With the rapid development of the business, the team is also expanding rapidly. We welcome you to join us and create world-class e-commerce products together. Hot positions: Android/iOS Senior Developer, Android/iOS expert, Java Architect, Product Manager (e-commerce background), test development… A lot of HC waiting for you ~ internal recommendation please send resume to >>> our email: [email protected] <<<, note my roster success rate is higher oh ~ 😘

The appendix

Appendix 1

1, NotificationListenerService

Notify the sender by E-mail (onNotificationPosted NotificationListenerService mainly provides monitoring system) and the ability to cancel (cancelNotification), and opened some notification operation interface.

methods role
getActiveNotifications() Gets all notification groups for the current notification bar
cancelAllNotifications() Delete all purgable notifications from the system (not resident notifications)
cancelNotification(String key) Delete a notification
snoozeNotification(String key, long durationMs) Sleep notification, you can clear the notification bar permanent notification
onNotificationPosted(StatusBarNotification sbn) Callback when notification is sent
onNotificationRemoved(StatusBarNotification sbn) Callback when notification is removed
onNotificationRankingUpdate(RankingMap rankingMap) Callback when notification ordering changes

2, StatusBarNotification

StatusBarNotification we NotificationListenerService# onNotificationPosted () interface to get inside to notify the instance, it basically has the following important method.

methods role
getId() Inform the id of the
getTag() Notification Tag, returns null if not set
getKey() The notification key, the unique identifier. Can be used to specify the deletion of a notification
getPostTime() The time when a notification is sent
getPackageName() Notify the corresponding package name
isClearable() FLAG_ONGOING_EVENT, FLAG_NO_CLEAR
getNotification() Get notification object