欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android?NotificationListenerService?通知服務(wù)原理解析

 更新時(shí)間:2022年11月18日 10:20:16   作者:黃林晴  
這篇文章主要為大家介紹了Android?NotificationListenerService?通知服務(wù)原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

在上一篇通知服務(wù)NotificationListenerService使用方法 中,我們已經(jīng)介紹了如何使用NotificationListenerService來監(jiān)聽消息通知,在最后我們還模擬了如何實(shí)現(xiàn)微信自動(dòng)搶紅包功能。

那么NotificationListenerService是如何實(shí)現(xiàn)系統(tǒng)通知監(jiān)聽的呢?(本篇源碼分析基于API 32)

NotificationListenerService方法集

NotificationLisenerService是Service的子類

public abstract class NotificationListenerService extends Service

除了Service的方法屬性外,NotificationListenerService還為我們提供了收到通知、通知被移除、連接到通知管理器等方法,如下圖所示。

一般業(yè)務(wù)中我們只關(guān)注有標(biāo)簽的那四個(gè)方法即可。

NotificationListenerService接收流程

既然NotificationListenerService是繼承自Service的,我們先來看它的onBind方法,代碼如下所示。

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

在onBind方法中返回了一個(gè)NotificationListenerWrapper實(shí)例,NotificationListenerWrapper對(duì)象是定義在NotificationListenerService中的一個(gè)內(nèi)部類。主要方法如下所示。

/** @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,當(dāng)我們看到Stub這一關(guān)鍵字的時(shí)候,就應(yīng)該知道這里是使用AIDL實(shí)現(xiàn)了跨進(jìn)程通信。

在NotificationListenerWrapper的onNotificationPosted中通過代碼

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

將消息發(fā)送出去,handler接受后,又調(diào)用NotificationListernerService的onNotificationPosted方法,進(jìn)而實(shí)現(xiàn)通知消息的監(jiān)聽。代碼如下所示。

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;
           ...
            }
        }
    }

那么,消息通知發(fā)送時(shí),又是如何與NotificationListenerWrapper通信的呢?

通知消息發(fā)送流程

當(dāng)客戶端發(fā)送一個(gè)通知的時(shí)候,會(huì)調(diào)用如下所示的代碼

notificationManager.notify(1, notification)

notify又會(huì)調(diào)用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();
    }
}

緊接著又會(huì)走到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);
    ...
 }

這個(gè)接口是在NotificationManagerService中實(shí)現(xiàn)的,接著我們轉(zhuǎn)到NotificationManagerService中去查看,相關(guān)主要代碼如下所示。

@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方法會(huì)走進(jìn)enqueueNotificationInternal方法,在方法最后會(huì)通過Handler發(fā)送一個(gè)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) {
        ...
        //構(gòu)造StatusBarNotification,用于分發(fā)監(jiān)聽服務(wù)
        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();
        }
        ...
        // 設(shè)置intent的白名點(diǎn),是否盛典、是否后臺(tái)啟動(dòng)等
        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) {
                ...
                //將通知加入隊(duì)列
                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最后又會(huì)發(fā)送一個(gè)PostNotificationRunable,

PostNotificationRunable源碼如下所示。

protected class PostNotificationRunnable implements Runnable {
        private final String key;
        PostNotificationRunnable(String key) {
            this.key = key;
        }
        @Override
        public void run() {
            synchronized (mNotificationLock) {
                try {
                    ...
                    //發(fā)送通知
                    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類中會(huì)調(diào)用notifyPostedLocked方法,這里你可能會(huì)有疑問:這里分明判斷notification.getSmallIcon()是否為null,不為null時(shí)才會(huì)進(jìn)入notifyPostedLocked方法。為什么這里直接默認(rèn)了呢?這是因?yàn)樵贏ndroid5.0中規(guī)定smallIcon不可為null,且NotificationListenerService僅適用于5.0以上,所以這里是必然會(huì)執(zhí)行到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);
                //循環(huán)通知每個(gè)ManagedServiceInfo對(duì)象
                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方法最終會(huì)調(diào)用notifyPosted方法,我們?cè)賮砜磏otifyPosted方法。

 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方法,最終會(huì)調(diào)用INotificationListerner的onNotificationPosted方法,這樣就通知到了NotificationListenerService的onNotificationPosted方法。

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

NotificationListenerService注冊(cè)

在NotificationListenerService中通過registerAsSystemService方法注冊(cè)服務(wù),代碼如下所示。

 @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對(duì)象注冊(cè)到NotificationManagerService中。如此就實(shí)現(xiàn)了對(duì)系統(tǒng)通知的監(jiān)聽。

總結(jié)

NotificationListenerService實(shí)現(xiàn)對(duì)系統(tǒng)通知的監(jiān)聽可以概括為三步:

  • NotificationListenerService將 NotificationListenerWrapper注冊(cè)到NotificationManagerService中。
  • 當(dāng)有通知被發(fā)送時(shí) ,NotificationManagerService跨進(jìn)程通知到每個(gè)NotificationListenerWrapper。
  • NotificationListenerWrapper中信息由NotificationListenerService類中的Handler中處理,從而調(diào)用NotificationListenerService中對(duì)應(yīng)的回調(diào)方法。

以上就是Android NotificationListenerService 通知服務(wù)原理解析的詳細(xì)內(nèi)容,更多關(guān)于Android NotificationListenerService的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android 實(shí)現(xiàn)文字左右對(duì)齊

    Android 實(shí)現(xiàn)文字左右對(duì)齊

    這篇文章主要介紹了Android 實(shí)現(xiàn)文字左右對(duì)齊效果的方法,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下
    2021-05-05
  • Android使用TextView實(shí)現(xiàn)無下劃線超鏈接的方法

    Android使用TextView實(shí)現(xiàn)無下劃線超鏈接的方法

    這篇文章主要介紹了Android使用TextView實(shí)現(xiàn)無下劃線超鏈接的方法,結(jié)合實(shí)例形式分析了Android中TextView超鏈接去除下劃線的相關(guān)實(shí)現(xiàn)技巧與注意事項(xiàng),需要的朋友可以參考下
    2016-08-08
  • ExpandableListView實(shí)現(xiàn)手風(fēng)琴效果

    ExpandableListView實(shí)現(xiàn)手風(fēng)琴效果

    這篇文章主要為大家詳細(xì)介紹了ExpandableListView實(shí)現(xiàn)手風(fēng)琴效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • android實(shí)現(xiàn)注冊(cè)頁(yè)面開發(fā)

    android實(shí)現(xiàn)注冊(cè)頁(yè)面開發(fā)

    這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)注冊(cè)頁(yè)面開發(fā),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Android ViewPager小圓點(diǎn)指示器

    Android ViewPager小圓點(diǎn)指示器

    這篇文章主要為大家詳細(xì)介紹了Android ViewPager小圓點(diǎn)指示器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • Android實(shí)現(xiàn)Gesture手勢(shì)識(shí)別用法分析

    Android實(shí)現(xiàn)Gesture手勢(shì)識(shí)別用法分析

    這篇文章主要介紹了Android實(shí)現(xiàn)Gesture手勢(shì)識(shí)別用法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android基于Gesture實(shí)現(xiàn)手勢(shì)識(shí)別的原理與具體實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2016-09-09
  • Android8.1 通過黑名單屏蔽系統(tǒng)短信和來電功能

    Android8.1 通過黑名單屏蔽系統(tǒng)短信和來電功能

    最近小編接到一個(gè)新的需求,需要將8.1 設(shè)備的來電功能和短信功能都屏蔽掉,特殊產(chǎn)品就是特殊定制。接下來通過本文給大家介紹Android8.1 通過黑名單屏蔽系統(tǒng)短信和來電功能,需要的朋友參考下吧
    2019-05-05
  • Android自定義控件仿QQ抽屜效果

    Android自定義控件仿QQ抽屜效果

    這篇文章主要為大家詳細(xì)介紹了Android自定義控件仿QQ抽屜效果的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • Android 沉浸式狀態(tài)欄與隱藏導(dǎo)航欄實(shí)例詳解

    Android 沉浸式狀態(tài)欄與隱藏導(dǎo)航欄實(shí)例詳解

    沉浸式狀態(tài)欄是指狀態(tài)欄與ActionBar顏色相匹配,隱藏導(dǎo)航欄,就是將導(dǎo)航欄隱藏,去掉下面的黑條。下面通過實(shí)例給大家詳解android沉浸式狀態(tài)欄與隱藏導(dǎo)航欄,感興趣的朋友一起看看
    2017-07-07
  • Android實(shí)現(xiàn)屏幕旋轉(zhuǎn)方法總結(jié)

    Android實(shí)現(xiàn)屏幕旋轉(zhuǎn)方法總結(jié)

    這篇文章主要介紹了Android實(shí)現(xiàn)屏幕旋轉(zhuǎn)方法,實(shí)例總結(jié)了屏幕旋轉(zhuǎn)的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-04-04

最新評(píng)論