Notification在Android系统中扮演着重要的角色,主要作用包括显示接收到的信息、显示应用程序的状态以及提醒用户进行某项操作。除了下拉状态栏显示的通知信息外,通知还能带有声音、振动、通知灯、图片图标等丰富的信息。它可以帮助用户快速了解和处理应用程序的实时信息,提高用户的使用体验和效率。
Notification通知框架主要由三部分组成:发送/更新/取消通知的应用APP、系统通知服务NotificationManagerService和 通知监听器NotificationListenerService。应用可以调用NotificationManager.notify(int,Notification)发送通知到系统框架层,框架层中的NotificationManagerService对通知进行处理,如判断是否拦截通知、播放声音、振动、调用通知灯等,再将通知发送给各个通知监听器NotificationListenerService。SystemUI作为最重要的通知监听器,它在接收到通知后会对通知UI进行更新显示。
二、Framework发送
Notification代码流程
应用在创建通知对应的NotificationChannel之后,就能调用NotificationManager.notify(int,Notification)发送通知。接下来我们分析Android 13中Notification相关的部分源码,了解通知从应用到系统框架,再从系统框架到通知监听器,最后从SystemUI监听器到显示通知的大概流程。
1、 APP通过NotificationManager.notify发送通知
2、 NotificationManager的notify最终会调用 notifyAsUser,notifyAsUser会调用fixNotification校验通知声音和尝试修复校验裁剪通知图标ICON,接着调用service的enqueueNotificationWithTag方法。service是INotificationManager类型的代理服务,通过它的enqueueNotificationWithTag方法把通知发送给通知服务处理。
3、 NotificationManagerService.java中的mService实现了INotificationManager.Stub,所以前面调用INotificationManager相关方法会调用到这里。
4、 mService的enqueueNotificationWithTag方法会调用到NotificationManagerService.enqueueNotificationInternal方法。
enqueueNotificationInternal首先会检查包名是否null、通知是否null、发起通知的应用是否有权限;接着还会尝试修复notification、检测通知channel、调整前台服务通知的importance控制通知是否显示;
还会将notification进一步封装为StatusBarNotification对象n和NotificationRecord对象r,、接着调用checkDisqualifyingFeatures检查通知速率和通知数量,最后将NotificationRecord对象r发送到一个异步线程 EnqueueNotificationRunnable 的run方法中去执行。
5、 EnqueueNotificationRunnable.run方法会定义通知自动取消的时间任务,更新通知Group;并且把NotificationRecord对象r添加进mEnqueuedNotifications。最后把对象r的一些信息传给一个PostNotificationRunnable对象并run方法里执行。
6、 PostNotificationRunnable.run方法会从前面添加了NotificationRecord对象r的mEnqueuedNotifications里取出NotificationRecord对象和StatusBarNotification对象、通知排序。
然后调用buzzBeepBlinkLocked(r)处理通知声音、振动、通知灯,最后调用mListeners.notifyPostedLocked(r, old)方法。
7、 NotificationListeners.notifyPostedLocked首先会做该通知是否显示的各种判断;根据通知生成NotificationRankingUpdate对象,该对象会post到监听器,用于通知排序;赋予Uri访问权限。最后如果显示通知的话会调用notifyPosted方法,其中参数info包含了注册的通知监听器服务,参数sbnToPost就是从NotificationRecord中提取的StatusBarNotification。
8、 NotificationListeners.notifyPosted会调用INotificationListener.onNotificationPosted方法
9、 NotificationListenerService.java中的NotificationListenerWrapper实现了
INotificationListener.Stub接口,所以INotificationListener.onNotificationPosted会调用到NotificationListenerWrapper的onNotificationPosted接口实现。
最后会发送MSG_ON_NOTIFICATION_POSTED消息给NotificationListenerService的MyHandler,MyHandler收到该消息后会调用NotificationListenerService的onNotificationPosted处理。
10、 NotificationListenerService是个抽象类,声明如下:
它的onNotificationPosted方法没有具体的实现,所以onNotificationPosted的实现,通常由NotificationListenerService的子类完成。
NotificationListenerService是所有通知监听类的父类,所有监听通知的类最终都会继承于它。通过调用它的onNotificationPosted方法把APP发送的通知分发给各个通知监听器。其中SystemUI作为最重要的通知监听器,在收到通知后会处理通知相关的UI显示。接下来我们继续分析SystemUI收到通知后显示通知的流程。
三、SystemUI接收显示Notification流程
1.前面分析到Framework通过调用NotificationListenerService的onNotificationPosted方法把APP发送的通知分发给各个通知监听器。SystemUI里的NotificationListenerWithPlugins类继承自NotificationListenerService,但NotificationListenerWithPlugins里面并没有实现onNotificationPosted方法。通过查找发现NotificationListener继承自NotificationListenerWithPlugins,并且实现了onNotificationPosted、onNotificationRemoved、onNotificationRankingUpdate等方法。
在Framework回调了NotificationListener的onNotificationPosted方法后,会调用NotificationListener.NotificationHandler的onNotificationPosted方法处理接收到StatusBarNotification。
2.NotificationEntryManager的mNotifListener实现了
NotificationListener.NotificationHandler的onNotificationPosted方法。接着调用addNotification添加通知。
3.addNotification 会继续调用addNotificationInternal。addNotificationInternal会调用updateRankingAndSort更新通知等级。
接着将StatusBarNotification封装成NotificationEntry对象entry,并调用
mNotificationRowBinderLazy.get().inflateViews(entry, null, mInflationCallback)构建填充通知布局。
4.这里执行inflateViews的是NotificationRowBinderImpl的inflateViews方法
如果entry的ExpandableNotificationRow不存在就会调用mRowInflaterTaskProvider.get().inflate(mContext, parent, entry, RowInflationFinishedListener)去填充通知布局,此处实际调用的是RowInflaterTask的inflate方法。
这里填充的布局是R.layout.status_bar_notification_row,它的根标签是ExpandableNotificationRow。
5.填充通知布局后会调用bindRow方法,该方法会和一些控制管理器绑定和继续完成填充通知布局。会依次调用NotifBindPipeline的manageRow方法 -> NotifBindPipeline.requestPipelineRun方法。
requestPipelineRun方法会发送START_PIPELINE_MSG消息给mMainHandler。
mMainHandler是NotifBindPipelineHandler类型,该消息会调用NotifBindPipeline的startPipeline方法。
startPipeline方法接着会调用mStage.executeStage方法。BindStage是个抽象类,具体实现是RowContentBindStage的executeStage方法。
RowContentBindStage的executeStage方法会调用mBinder.bindContent方法。mBinder是实现NotificationRowContentBinder接口的对象,具体实现类为NotificationContentInflater。也就是这里实际调用的是NotificationContentInflater的bindContent方法。该方法会调用异步线程AsyncInflationTask,AsyncInflationTask会依次调用inflateSmartReplyViews、apply -> applyRemoteView方法去加载通知布局。
之后apply还会调用finishIfDone完成填充,主要是填充ExpandableNotificationRow的privateLayout和publicLayout。
6.在上面填充完通知布局最后会调用NotificationRowBinderImpl的updateRow和inflateContentViews方法,updateRow方法会绑定一些点击事件,而inflateContentViews方法最后会回调inflationCallback.onAsyncInflationFinished方法。
实际调用的是NotificationEntryManager.mInflationCallback的onAsyncInflationFinished方法,该方法会继续调用NotificationEntryManager.updateNotifications方法对通知的容器进行更新。
updateNotifications会调用mPresenter.updateNotificationViews(reason)。
7.mPresenter是实现了NotificationPresenter接口的类。mPresenter.updateNotificationViews方法的实现代码是StatusBarNotificationPresenter的updateNotificationViews方法。该方法会依次调用NotificationViewHierarchyManager的updateNotificationViews方法和NotificationPanelViewController的updateNotificationViews方法更新通知。
8.NotificationViewHierarchyManager的updateNotificationViews方法会先取出可见的通知NotificationEntry。
然后再根据需要调用mListContainer.removeContainerView(View)移除通知视图或调用mListContainer.addContainerView(View)将通知视图添加到通知容器NotificationStackScrollLayout,也就是将通知视图移除或添加到通知栏中,并对通知排序。
9.NotificationPanelViewController的updateNotificationViews方法除了更新NotificationStackScrollLayout视图的信息,还会调用NotificationIconAreaController的updateNotificationIcons()方法更新各种Icon。
这几个更新Icon的方法都是调用updateIconsForLayout方法来实现的。
updateIconsForLayout先从mNotificationEntries(来源于mNotificationStackScrollLayoutController)取出NotificationEntry,然后把NotificationEntry里的StatusBarIconView取出添加到toShow。
最后调用hostLayout.addView(v, i, params)添加Icon到前面更新Icon的方法传入的NotificationIconContainer布局里。
到此通知已经显示在通知栏和状态栏中,整个流程分析完毕。
四、小结
本文简要分析了通知从发送到显示的基本流程。实际上,与通知(Notification)相关的代码相对众多,因此一些特定业务场景并未进行深入剖析,很多细节代码需要遇到具体问题再具体分析。对于这些内容感兴趣的朋友,可以针对特定的子业务流程展开进一步的分析。
参考链接:
https://developer.android.com/develop/ui/views/notifications
https://developer.android.com/reference/android/service/notification/NotificationListenerService
https://www.jianshu.com/p/914092161b4e
https://blog.csdn.net/qq_34211365/article/details/103398511
https://blog.csdn.net/z1804362542/article/details/127132720
https://blog.csdn.net/xiaowanbiao123/article/details/132654468
➤ 往期推荐
·使用honggfuzz进行Android模糊测试
·蓝牙安全漏洞BLUFFS的原理解析及影响范围
·Android本地服务漏洞挖掘技术 - 下篇
原文始发于微信公众号(OPPO安珀实验室):Android Notification从发送到显示的流程简析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论