網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
前言
在上一篇通知服務(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)方法。
原文鏈接:https://juejin.cn/post/7166864980632371207
相關(guān)推薦
- 2022-10-25 Git中smart?Checkout與force?checkout的區(qū)別及說明_相關(guān)技巧
- 2022-04-26 C#新特性之可空引用類型_C#教程
- 2022-03-26 oracle自動(dòng)統(tǒng)計(jì)信息時(shí)間的修改過程記錄_oracle
- 2022-12-13 Android?DataBinding單向數(shù)據(jù)綁定深入探究_Android
- 2023-04-19 Android開發(fā)服務(wù)Service全面講解_Android
- 2022-01-12 解決element-ui 日期選擇器提交后臺(tái)數(shù)據(jù)不準(zhǔn)確問題
- 2023-03-27 基于Unity3D實(shí)現(xiàn)仿真時(shí)鐘詳解_C#教程
- 2022-12-24 Kotlin?Channel處理多個(gè)數(shù)據(jù)組合的流_Android
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支