本文是基于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为notificationIconsNotificationIconContainer就是通知图标容器,对应于上面代码的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之通知图标控制相关推荐

  1. Android 9.0系统源码_SystemUI(四)通知图标控制器

    前言 上一篇我们具体分析了状态栏上状态图标,例如 wifi.蓝牙等图标的控制流程,本篇文章我们继续来分析下状态栏上通知图标的控制流程.主要分析当一个新通知来临时,新通知的图标是如何一步步显示到状态栏上 ...

  2. Android 9.0系统源码_SystemUI(三)系统状态图标控制

    前言 上一篇我们具体分析了系统状态栏StatusBar的创建过程,其中状态栏视图就存储在CollapsedStatusBarFragment中,这个视图被添加到id为status_bar_contai ...

  3. Android 10 状态栏通知图标和下拉状态栏图标为白色问题

    前言 安装第三方应用,会在状态栏上面和下拉状态栏通知图标显示白色 原因 因为google在android5.0上面做了限制,为了统一系统风格.之后的状态栏icon就不能够随便用一张色彩丰富的图片了,只 ...

  4. Android 5.0状态栏通知图标的实现

    Android 5.0状态栏通知图标的实现 我之前的博客文章中有一片是介绍了关于Android5.0 下拉通知栏快捷开关的添加,文章牵扯到一个知识点就是Android 5.0状态栏通知图标的实现.那么 ...

  5. Android SystemUI 状态栏网络图标显示分析(Android 11)

    最近看了一个wifi, ethernet切换,状态栏图表显示的问题.记录一下追踪由于网络状态变化,SystemUI 状态栏网络图标显示的流程. 先看一下SystemUI这边: /frameworks/ ...

  6. 解决Android高版本状态栏通知图标白底问题

    最近临时转换角色修改Android系统源码,没有搞过一看源码一脸懵逼,代码太多了,关键是无法跟studio一样直接进行代码调转,折腾了一天才找到这个问题要修改的地方,其实就是Google为了规范统一和 ...

  7. android设置状态栏字体颜色vivo,状态栏通知图标颜色统一_vivo Xplay3S_手机Android频道-中关村在线...

    顶部状态栏是用来显示消息和程序通知的地方,在之前1.0版本中,信号.时间.电量.wifi以及蓝牙等这些图标都是以白色来显示,而其他通知则会以起自身的图标颜色来显示,这样会使得顶部状态在色彩上显得很杂乱 ...

  8. 如何关闭任务栏中chrome浏览器的通知图标

    那如何关闭chrome浏览器的通知图标呢? 1.在chrome浏览器地址栏中输入chrome://flags 2.在出现的标签中按下"ctrl+F"调出搜索框,输入"通知 ...

  9. Android Studio系统状态栏,设置setSmallIcon通知图标无效问题及解决方案

    关于设置setSmallIcon通知图标无效问题 [分析] targetSdkVersion 是 Android 系统提供前向兼容的主要手段(即:新版本SDK手机兼容旧版本SDK工程).这是什么意思呢 ...

最新文章

  1. 毕向东Java基础教程(适合初学者入门的Java基础视频)
  2. 【数据结构与算法】7.位图算法、12306抢票算法
  3. 服务器flask远程访问_在Flask中使用什么API来检查远程(其他)服务器的连接?...
  4. WindowsPhone 7.8 Tiles 1 : WideTile
  5. java多线程 文件夹_Java多线程遍历文件夹,广度遍历加多线程加深度遍历结合
  6. 数组删除一行_一行Python代码能做出哪些神器的事情
  7. python grouo by_在python中实现数据库下group by功能
  8. 二、yii的入口与app应用实例(Yii::app())
  9. 10.Hello World 的 Java 项目创建和项目配置文件讲解
  10. Fabric学习--环境搭建
  11. foobar2000播放APE格式音乐的解决办法
  12. 智能额温枪软件设计红外测温仪方案开发
  13. 浙江大学计算机2018分数线,浙江大学2018多少录取分数线
  14. 淘宝联盟 淘宝客私域用户管理 百川SDK 接入简介
  15. 粤嵌6818开发板项目
  16. fsleyes -- 一款多功能影像数据查看器
  17. ajax传递数组到后台时为空,ajax传递数组,后台接收为null解决方法
  18. android java pbo_Android OpenGL ES 3.0 PBO而不是glReadPixels()
  19. qrcodejs华为手机无法识别二维码解决方法
  20. 2022西藏最新道路运输安全员模拟考试试题及答案

热门文章

  1. ECharts x坐标轴(xAxis)使用详解
  2. 腾讯课堂课程汇总 CourseList(1000000-1001000)
  3. OSChina 周二乱弹 —— C 语言是个女的?
  4. Python爬虫获取豆瓣电影TOP250
  5. jdk1.8 在綫英文+有道翻譯版
  6. python人文社科研究_人文社科论文写作数据分析利器|SPSS+Stata+Endnote+Python
  7. C语言解决一个人有100元钱,打算买100只鸡。到市场上面一看,公鸡一只3元,母鸡一只5元,小鸡3只一元,试求用100元买100只鸡,各买多少合适?
  8. vue 微信公众号获取定位经纬度 腾讯地图逆地址解析为具体地址
  9. Ubuntu释放单张显卡显存
  10. linux下shell脚本启动其他可执行程序