SystemUI之通知图标控制
本文是基于Android 10源码分析的。
SystemUI之状态图标控制 分析了状态栏上状态图标(例如 wifi, bt)的控制流程,比较简单。本文来分析下状态栏上通知图标的控制流程,主要分析当一个新通知来临时,新通知的图标是如何一步步显示到状态上的。
通知图标控制器
从SystemUI之状态图标控制可知,状态图标是由一个叫StatusBarIconController
接口控制显示的,而通知图标区域也有一个控制器,叫NotificationIconAreaController
(它不是一个接口)。
在NotificationIconAreaController
的构造函数中会调用如下方法来创建通知图标的容器
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.javaprotected void initializeNotificationAreaViews(Context context) {// ...LayoutInflater layoutInflater = LayoutInflater.from(context);// 实例化通知图标区域视图mNotificationIconArea = inflater.inflate(R.layout.notification_icon_area, null);// 这个才是真正存放通知图标的父容器mNotificationIcons = mNotificationIconArea.findViewById(R.id.notificationIcons);// ...}
这里加载了notification_icon_are.xml
布局,来看下这个布局
<com.android.keyguard.AlphaOptimizedLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/notification_icon_area_inner"android:layout_width="match_parent"android:layout_height="match_parent"android:clipChildren="false"><com.android.systemui.statusbar.phone.NotificationIconContainerandroid:id="@+id/notificationIcons"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_alignParentStart="true"android:gravity="center_vertical"android:orientation="horizontal"android:clipChildren="false"/>
</com.android.keyguard.AlphaOptimizedLinearLayout>
ID为notificationIcons
的NotificationIconContainer
就是通知图标容器,对应于上面代码的mNotificationIcons
变量。
初始化通知图标区域
既然NotificationIconAreaController
自己创建了通知图标容器,那么就需要加入到状态栏视图中,这个动作在创建状态栏视图后完成的
// packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.javaprotected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {// ...FragmentHostManager.get(mStatusBarWindow).addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {CollapsedStatusBarFragment statusBarFragment =(CollapsedStatusBarFragment) fragment;// 把控制器中的通知容器加入到状态栏的容器中statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);// ...}).getFragmentManager().beginTransaction()// CollapsedStatusBarFragment实现了状态栏的添加.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),CollapsedStatusBarFragment.TAG).commit(); }
调用的就是CollapsedStatusBarFragment#initNotificationIconArea()
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.javapublic void initNotificationIconArea(NotificationIconAreaControllernotificationIconAreaController) {// 通知图标区域ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);// 这个才是通知图标的父容器mNotificationIconAreaInner =notificationIconAreaController.getNotificationInnerAreaView();if (mNotificationIconAreaInner.getParent() != null) {((ViewGroup) mNotificationIconAreaInner.getParent()).removeView(mNotificationIconAreaInner);}// 把通知图标父容器添加到通知图标区域里notificationIconArea.addView(mNotificationIconAreaInner);// 省略处理中心图标区域的代码// 这里其实显示了通知图标区域和中心图标区域showNotificationIconArea(false);}
监听通知的服务端
当一条新通知发送后,它会存储到通知服务端,也就是NotificationManagerService
,那么SystemUI是如何知道新通知来临的。这就需要SystemUI向ActivityManagerService
注册一个"服务"(一个Binder)。
这个"服务"就相当于客户端SystemUI在服务端ActivityManagerService
注册的一个回调。当有通知来临的时候,就会通过这个"服务"通知SystemUI。
这个注册是在StatusBar#start()
中完成的。
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.javapublic void start() {// 向通知服务端注册一个"服务",用于接收通知信息的回调mNotificationListener = Dependency.get(NotificationListener.class);mNotificationListener.registerAsSystemService();}
来看下这个注册的实现
// frameworks/base/core/java/android/service/notification/NotificationListenerService.javapublic void registerAsSystemService(Context context, ComponentName componentName,int currentUser) throws RemoteException {if (mWrapper == null) {// 这就是要注册的Binder,也就一个回调mWrapper = new NotificationListenerWrapper();}INotificationManager noMan = getNotificationInterface();// 向通知的服务端注册回调noMan.registerListener(mWrapper, componentName, currentUser);}
这个"服务"就是NotificationListenerWrapper
。
注册成功后,就会回调
NotificationListenerWrapper#NotificationListenerWrapper()
方法,并会附带所有的通知信息。
显示通知图标
当一条新的通知来临的时候,通知的服务端会通过NotificationListenerWrapper#onNotificationPosted()
进行回调,而最终会调用NotificationListener#onNotificationPosted()
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.javapublic void onNotificationPosted(final StatusBarNotification sbn,final RankingMap rankingMap) {if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {// 在主线程中进行更新Dependency.get(Dependency.MAIN_HANDLER).post(() -> {processForRemoteInput(sbn.getNotification(), mContext);String key = sbn.getKey();boolean isUpdate =mEntryManager.getNotificationData().get(key) != null;if (isUpdate) {// 更新通知操作mEntryManager.updateNotification(sbn, rankingMap);} else {// 添加新通知操作mEntryManager.addNotification(sbn, rankingMap);}});}}
这里讨论添加新通知的操作,它调用的是NotificationManager#addNotification()
方法,而内部是通过addNotificationInternal()
实现的
private void addNotificationInternal(StatusBarNotification notification,NotificationListenerService.RankingMap rankingMap) throws InflationException {// ...// NotificationEntry就代表一个通知实例NotificationEntry entry = new NotificationEntry(notification, ranking);// 异步加载视图,并绑定通知信息,由NotificationRowBinderImpl实现requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,REASON_CANCEL));// ...}
首先为通知创建一个NotificationEntry
实例,然后再通过NotificationRowBinderImpl#inflateViews()
加载通知视图,绑定通知信息,并在通知栏添加通知视图,以及在状态栏添加通知图标。
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.javapublic void inflateViews(NotificationEntry entry,Runnable onDismissRunnable)throws InflationException {// ...if (entry.rowExists()) {} else {// 给通知创建图标entry.createIcons(mContext, sbn);// 异步加载通知视图new RowInflaterTask().inflate(mContext, parent, entry,// 加载完成的回调,这里的加载指的仅仅是一个空视图row -> {// 绑定监听事件和回调bindRow(entry, pmUser, sbn, row, onDismissRunnable);// 在视图上更新通知信息updateNotification(entry, pmUser, sbn, row);});}}
RowInflaterTask#inflate()
会使用status_bar_notification_row.xml
布局创建一个通知视图,但是并没有把它加入到父容器中,更没有把把通知信息更新到视图中,这些工作都是在回调中完成的。
第一个回调bindRow()
,会为视图绑定各种监听事件以及回调
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.javaprivate void bindRow(NotificationEntry entry, PackageManager pmUser,StatusBarNotification sbn, ExpandableNotificationRow row,Runnable onDismissRunnable) {// ...// 刚才只是创建了视图,并没有绑定数据,这里就是设置绑定数据后的回调,这个回调是由NotificationEntryManager实现row.setInflationCallback(mInflationCallback);// ...}
这里只列出了与本文分析相关的回调,这个回调是在视图与通知信息绑定后的回调。
第二个回调updateNotification()
,用数据更新视图,更新完成后就会进行回调刚才绑定的回调事件,而这个回调是由NotificationEntryManager#onAsyncInflationFinished()
实现的
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.javapublic void onAsyncInflationFinished(NotificationEntry entry,@InflationFlag int inflatedFlags) {mPendingNotifications.remove(entry.key);if (!entry.isRowRemoved()) {boolean isNew = mNotificationData.get(entry.key) == null;if (isNew) {// ...if (mPresenter != null) {// 显示视图// 这个由StatusBarNotificationPresenter实现mPresenter.updateNotificationViews();}// ...} else {}}}
数据已经准备完毕,那么现在就是要显示视图了,这个视图包括通知栏里的通知,以及状态栏时的通知图标。这个由StatusBarNotificationPresenter#updateNotificationViews()
实现
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.javapublic void updateNotificationViews() {// ...// 1\. 把通知视图添加到通知面版的通知栏中mViewHierarchyManager.updateNotificationViews();// 这里不仅仅更新了通知面版的通知视图,也更新了状态栏的通知图标mNotificationPanel.updateNotificationViews();// ...}public void updateNotificationViews() {// ...省略更新通知栏的相关视图的代码updateShowEmptyShadeView();// 2\. 调用mIconAreaController更新了状态栏通知图标// 其实就是调用 mIconAreaController.updateNotificationIcons();mNotificationStackScroller.updateIconAreaViews();}
首先是往通知栏里添加通知视图,然后再更新状态栏视图。现在只看下如何向状态栏添加通知图标的,它最终是由NotificationIconAreaController#updateIconsForLayout()
实现的
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.javaprivate void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function,NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority,boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia,boolean hideCenteredIcon) {// toShow保存即将显示的图标ArrayList<StatusBarIconView> toShow = new ArrayList<>(mNotificationScrollLayout.getChildCount());// 过滤通知,并保存需要显示的通知图标for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {// 获取一个通知视图View view = mNotificationScrollLayout.getChildAt(i);if (view instanceof ExpandableNotificationRow) {NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry();if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed,hideRepliedMessages, hideCurrentMedia, hideCenteredIcon)) {// 获取图标StatusBarIconView iconView = function.apply(ent);if (iconView != null) {toShow.add(iconView);}}}}// ...// 把需要显示的图标添加到hostLayout中final FrameLayout.LayoutParams params = generateIconLayoutParams();for (int i = 0; i < toShow.size(); i++) {StatusBarIconView v = toShow.get(i);// The view might still be transiently added if it was just removed and added againhostLayout.removeTransientView(v);if (v.getParent() == null) {if (hideDismissed) {v.setOnDismissListener(mUpdateStatusBarIcons);}hostLayout.addView(v, i, params);}}// ...}
纵观整个过程,它的原理是根据通知栏的通知视图,来获取通知图标,然后经过一系列的过滤过程,最终把图标添加到状态栏通知图标容器中。
结束
本文简要分析了通知图标的显示流程,其中穿插提到了通知栏的通知视图的添加过程。掌握了大纲,就可以对细节进行考究,甚至对SystemUI进行定制。
不管好与不好,动动你的小手为小编点个赞吧,你的点赞将是小编最大的动力。
SystemUI之通知图标控制相关推荐
- Android 9.0系统源码_SystemUI(四)通知图标控制器
前言 上一篇我们具体分析了状态栏上状态图标,例如 wifi.蓝牙等图标的控制流程,本篇文章我们继续来分析下状态栏上通知图标的控制流程.主要分析当一个新通知来临时,新通知的图标是如何一步步显示到状态栏上 ...
- Android 9.0系统源码_SystemUI(三)系统状态图标控制
前言 上一篇我们具体分析了系统状态栏StatusBar的创建过程,其中状态栏视图就存储在CollapsedStatusBarFragment中,这个视图被添加到id为status_bar_contai ...
- Android 10 状态栏通知图标和下拉状态栏图标为白色问题
前言 安装第三方应用,会在状态栏上面和下拉状态栏通知图标显示白色 原因 因为google在android5.0上面做了限制,为了统一系统风格.之后的状态栏icon就不能够随便用一张色彩丰富的图片了,只 ...
- Android 5.0状态栏通知图标的实现
Android 5.0状态栏通知图标的实现 我之前的博客文章中有一片是介绍了关于Android5.0 下拉通知栏快捷开关的添加,文章牵扯到一个知识点就是Android 5.0状态栏通知图标的实现.那么 ...
- Android SystemUI 状态栏网络图标显示分析(Android 11)
最近看了一个wifi, ethernet切换,状态栏图表显示的问题.记录一下追踪由于网络状态变化,SystemUI 状态栏网络图标显示的流程. 先看一下SystemUI这边: /frameworks/ ...
- 解决Android高版本状态栏通知图标白底问题
最近临时转换角色修改Android系统源码,没有搞过一看源码一脸懵逼,代码太多了,关键是无法跟studio一样直接进行代码调转,折腾了一天才找到这个问题要修改的地方,其实就是Google为了规范统一和 ...
- android设置状态栏字体颜色vivo,状态栏通知图标颜色统一_vivo Xplay3S_手机Android频道-中关村在线...
顶部状态栏是用来显示消息和程序通知的地方,在之前1.0版本中,信号.时间.电量.wifi以及蓝牙等这些图标都是以白色来显示,而其他通知则会以起自身的图标颜色来显示,这样会使得顶部状态在色彩上显得很杂乱 ...
- 如何关闭任务栏中chrome浏览器的通知图标
那如何关闭chrome浏览器的通知图标呢? 1.在chrome浏览器地址栏中输入chrome://flags 2.在出现的标签中按下"ctrl+F"调出搜索框,输入"通知 ...
- Android Studio系统状态栏,设置setSmallIcon通知图标无效问题及解决方案
关于设置setSmallIcon通知图标无效问题 [分析] targetSdkVersion 是 Android 系统提供前向兼容的主要手段(即:新版本SDK手机兼容旧版本SDK工程).这是什么意思呢 ...
最新文章
- 毕向东Java基础教程(适合初学者入门的Java基础视频)
- 【数据结构与算法】7.位图算法、12306抢票算法
- 服务器flask远程访问_在Flask中使用什么API来检查远程(其他)服务器的连接?...
- WindowsPhone 7.8 Tiles 1 : WideTile
- java多线程 文件夹_Java多线程遍历文件夹,广度遍历加多线程加深度遍历结合
- 数组删除一行_一行Python代码能做出哪些神器的事情
- python grouo by_在python中实现数据库下group by功能
- 二、yii的入口与app应用实例(Yii::app())
- 10.Hello World 的 Java 项目创建和项目配置文件讲解
- Fabric学习--环境搭建
- foobar2000播放APE格式音乐的解决办法
- 智能额温枪软件设计红外测温仪方案开发
- 浙江大学计算机2018分数线,浙江大学2018多少录取分数线
- 淘宝联盟 淘宝客私域用户管理 百川SDK 接入简介
- 粤嵌6818开发板项目
- fsleyes -- 一款多功能影像数据查看器
- ajax传递数组到后台时为空,ajax传递数组,后台接收为null解决方法
- android java pbo_Android OpenGL ES 3.0 PBO而不是glReadPixels()
- qrcodejs华为手机无法识别二维码解决方法
- 2022西藏最新道路运输安全员模拟考试试题及答案
热门文章
- ECharts x坐标轴(xAxis)使用详解
- 腾讯课堂课程汇总 CourseList(1000000-1001000)
- OSChina 周二乱弹 —— C 语言是个女的?
- Python爬虫获取豆瓣电影TOP250
- jdk1.8 在綫英文+有道翻譯版
- python人文社科研究_人文社科论文写作数据分析利器|SPSS+Stata+Endnote+Python
- C语言解决一个人有100元钱,打算买100只鸡。到市场上面一看,公鸡一只3元,母鸡一只5元,小鸡3只一元,试求用100元买100只鸡,各买多少合适?
- vue 微信公众号获取定位经纬度 腾讯地图逆地址解析为具体地址
- Ubuntu释放单张显卡显存
- linux下shell脚本启动其他可执行程序