代码流程

1、先看UI显示,StatuBar加载 CollapsedStatusBarFragment 替换 status_bar_container(状态栏通知显示区域)

SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java

  1. FragmentHostManager.get(mStatusBarWindow)
  2. .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
  3. CollapsedStatusBarFragment statusBarFragment =
  4. (CollapsedStatusBarFragment) fragment;
  5. statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
  6. mStatusBarView = (PhoneStatusBarView) fragment.getView();
  7. mStatusBarView.setBar(this);
  8. mStatusBarView.setPanel(mNotificationPanel);
  9. mStatusBarView.setScrimController(mScrimController);
  10. mStatusBarView.setBouncerShowing(mBouncerShowing);
  11. setAreThereNotifications();
  12. checkBarModes();
  13. /// M: add for plmn display feature @{
  14. attachPlmnPlugin();
  15. ///@}
  16. }).getFragmentManager()
  17. .beginTransaction()
  18. .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
  19. CollapsedStatusBarFragment.TAG)
  20. .commit();

statusBarFragment.initNotificationIconArea(mNotificationIconAreaController) 初始化通知栏区域,这是我们关心的

mStatusBarView.setBar(this) 传递statusBar处理下拉事件

mStatusBarView.setPanel(mNotificationPanel) 传递 NotificationPanelView 显示下拉UI控制

2、跟进 CollapsedStatusBarFragment 中,先看布局文件 status_bar.xml

  1. 1notification_lights_out---ImageView默认gone
  2. 2status_bar_contents--LinearLayout
  3. notification_icon_area--FrameLayout
  4. system_icon_area--LinearLayout
  5. system_icons.xml(蓝牙、wifiVPN、网卡、SIM卡信号、飞行模式等) 电池
  6. clock--Clock.java
  7. 3emergency_cryptkeeper_text--ViewStub(延迟加载 紧急电话文字)

这就是我们看到的statusBar的布局,本篇只关心 notification_icon_area,其它的以后再进行分析。继续看到之前的 initNotificationIconArea()

SystemUI\src\com\android\systemui\statusbar\phone\CollapsedStatusBarFragment.java

  1. public void initNotificationIconArea(NotificationIconAreaController
  2. notificationIconAreaController) {
  3. ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
  4. mNotificationIconAreaInner =
  5. notificationIconAreaController.getNotificationInnerAreaView();
  6. if (mNotificationIconAreaInner.getParent() != null) {
  7. ((ViewGroup) mNotificationIconAreaInner.getParent())
  8. .removeView(mNotificationIconAreaInner);
  9. }
  10. notificationIconArea.addView(mNotificationIconAreaInner);
  11. // Default to showing until we know otherwise.
  12. showNotificationIconArea(false);
  13. }

获取到 notification_icon_area,FrameLayout转为ViewGroup,调用 notificationIconAreaController 获取通知要显示的view(LinearLayout),

如果已经有显示的view,通过 view 父布局将其自身remove,然后再重新addView。最后将 mNotificationIconAreaInner 显示出来(设置透明度为1,visibility为VISIBLE)

可以看到 CollapsedStatusBarFragment 中定义了几个如下的方法。

  1. public void hideSystemIconArea(boolean animate) {
  2. animateHide(mSystemIconArea, animate);
  3. }
  4. public void showSystemIconArea(boolean animate) {
  5. animateShow(mSystemIconArea, animate);
  6. }
  7. public void hideNotificationIconArea(boolean animate) {
  8. animateHide(mNotificationIconAreaInner, animate);
  9. }
  10. public void showNotificationIconArea(boolean animate) {
  11. animateShow(mNotificationIconAreaInner, animate);
  12. }

当状态栏下拉时,状态栏中的图标icon会慢慢的变成透明和不可见,就是通过hideSystemIconArea(true), hideNotificationIconArea(true)

3、接下来,我们需要跟进 getNotificationInnerAreaView()方法中看看通知栏icon对应的容器

SystemUI\src\com\android\systemui\statusbar\phone\NotificationIconAreaController.java

  1. public View getNotificationInnerAreaView() {
  2. return mNotificationIconArea;
  3. }
  4. protected void initializeNotificationAreaViews(Context context) {
  5. reloadDimens(context);
  6. LayoutInflater layoutInflater = LayoutInflater.from(context);
  7. mNotificationIconArea = inflateIconArea(layoutInflater);
  8. mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById(
  9. R.id.notificationIcons);
  10. mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();
  11. }
  12. protected View inflateIconArea(LayoutInflater inflater) {
  13. return inflater.inflate(R.layout.notification_icon_area, null);
  14. }
  15. //notification_icon_area.xml
  16. <com.android.keyguard.AlphaOptimizedLinearLayout
  17. xmlns:android="http://schemas.android.com/apk/res/android"
  18. android:id="@+id/notification_icon_area_inner"
  19. android:layout_width="match_parent"
  20. android:layout_height="match_parent" >
  21. <com.android.systemui.statusbar.phone.NotificationIconContainer
  22. android:id="@+id/notificationIcons"
  23. android:layout_width="match_parent"
  24. android:layout_height="match_parent"
  25. android:layout_alignParentStart="true"
  26. android:gravity="center_vertical"
  27. android:orientation="horizontal"/>
  28. </com.android.keyguard.AlphaOptimizedLinearLayout>

好了,观察上面的代码,现在基本上已经理清 notification_icon_area 的布局结构了

notification_icon_area(FrameLayout) 中添加 notification_icon_area_inner(LinearLayout),

每一个通知对应的bean为 NotificationData,创建 Notification 添加到 NotificationIconContainer(FrameLayout)中

4、紧接着我们就来看下 Notification 的监听加载流程,回到 statusBar 的start()中注册 NotificationListenerWithPlugins 作为系统service监听通知消息

  1. try {
  2. mNotificationListener.registerAsSystemService(mContext,
  3. new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
  4. UserHandle.USER_ALL);
  5. } catch (RemoteException e) {
  6. Log.e(TAG, "Unable to register notification listener", e);
  7. }
  8. private final NotificationListenerWithPlugins mNotificationListener =
  9. new NotificationListenerWithPlugins() {
  10. @Override
  11. public void onListenerConnected() {
  12. ...... services成功启动,获取当前处于活动状态的通知(没被移除的通知),添加到通知栏,此处应该是重启后重新加载
  13. }
  14. @Override
  15. public void onNotificationPosted(final StatusBarNotification sbn,
  16. final RankingMap rankingMap) {
  17. ...... 收到通知消息,添加或者修改
  18. if (isUpdate) {
  19. updateNotification(sbn, rankingMap);
  20. } else {
  21. addNotification(sbn, rankingMap);
  22. }
  23. }
  24. @Override
  25. public void onNotificationRemoved(StatusBarNotification sbn,
  26. final RankingMap rankingMap) {
  27. ...... 移除通知消息
  28. if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
  29. final String key = sbn.getKey();
  30. mHandler.post(() -> removeNotification(key, rankingMap));
  31. }
  32. }
  33. @Override
  34. public void onNotificationRankingUpdate(final RankingMap rankingMap) {
  35. ..... 通知的排序优先级改变,修改通知位置
  36. if (rankingMap != null) {
  37. RankingMap r = onPluginRankingUpdate(rankingMap);
  38. mHandler.post(() -> updateNotificationRanking(r));
  39. }
  40. }
  41. };

继续来看下 addNotification()方法

  1. public void addNotification(StatusBarNotification notification, RankingMap ranking)
  2. throws InflationException {
  3. String key = notification.getKey();
  4. if (true/**DEBUG*/) Log.d(TAG, "addNotification key=" + key);
  5. mNotificationData.updateRanking(ranking);
  6. Entry shadeEntry = createNotificationViews(notification);
  7. ......
  8. }

可以看到是通过 createNotificationViews()来创建通知 View对象,内部继续调用 inflateViews()

  1. protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
  2. throws InflationException {
  3. if (DEBUG) {
  4. Log.d(TAG, "createNotificationViews(notification=" + sbn);
  5. }
  6. NotificationData.Entry entry = new NotificationData.Entry(sbn);
  7. Dependency.get(LeakDetector.class).trackInstance(entry);
  8. entry.createIcons(mContext, sbn);
  9. // Construct the expanded view.
  10. inflateViews(entry, mStackScroller);
  11. return entry;
  12. }
  13. protected void inflateViews(Entry entry, ViewGroup parent) {
  14. PackageManager pmUser = getPackageManagerForUser(mContext,
  15. entry.notification.getUser().getIdentifier());
  16. final StatusBarNotification sbn = entry.notification;
  17. if (entry.row != null) {
  18. entry.reset();
  19. updateNotification(entry, pmUser, sbn, entry.row);
  20. } else {
  21. new RowInflaterTask().inflate(mContext, parent, entry,
  22. row -> {
  23. bindRow(entry, pmUser, sbn, row);
  24. updateNotification(entry, pmUser, sbn, row);
  25. });
  26. }
  27. }

看到上面的方法中,entry在 createNotificationViews 中创建,只赋值了icons, entry.row 为null,进入 RowInflaterTask 中

SystemUI\src\com\android\systemui\statusbar\notification\RowInflaterTask.java

  1. public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
  2. RowInflationFinishedListener listener) {
  3. mListener = listener;
  4. AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
  5. mEntry = entry;
  6. entry.setInflationTask(this);
  7. inflater.inflate(R.layout.status_bar_notification_row, parent, this);
  8. }

这里我们得到了 Notification 对应的layout为 status_bar_notification_row.xml

回调方法中将 row 和 entry 绑定,继续再调用 updateNotification(),注意这个方法是四个参数的,该类中还有重载方法是两个参数的。

  1. private void updateNotification(Entry entry, PackageManager pmUser,
  2. StatusBarNotification sbn, ExpandableNotificationRow row) {
  3. .....
  4. entry.row = row;
  5. entry.row.setOnActivatedListener(this);
  6. boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
  7. mNotificationData.getImportance(sbn.getKey()));
  8. boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded;
  9. row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
  10. row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
  11. row.updateNotification(entry);
  12. }

紧接着调用了 ExpandableNotificationRow的 updateNotification(),内部继续调用 NotificationInflater.inflateNotificationViews()

SystemUI\src\com\android\systemui\statusbar\notification\NotificationInflater.java

  1. @VisibleForTesting
  2. void inflateNotificationViews(int reInflateFlags) {
  3. if (mRow.isRemoved()) {
  4. // We don't want to reinflate anything for removed notifications. Otherwise views might
  5. // be readded to the stack, leading to leaks. This may happen with low-priority groups
  6. // where the removal of already removed children can lead to a reinflation.
  7. return;
  8. }
  9. StatusBarNotification sbn = mRow.getEntry().notification;
  10. new AsyncInflationTask(sbn, reInflateFlags, mRow, mIsLowPriority,
  11. mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
  12. mCallback, mRemoteViewClickHandler).execute();
  13. }

new AsyncInflationTask().execute();

  1. @Override
  2. protected InflationProgress doInBackground(Void... params) {
  3. try {
  4. final Notification.Builder recoveredBuilder
  5. = Notification.Builder.recoverBuilder(mContext,
  6. mSbn.getNotification());
  7. Context packageContext = mSbn.getPackageContext(mContext);
  8. Notification notification = mSbn.getNotification();
  9. if (mIsLowPriority) {
  10. int backgroundColor = mContext.getColor(
  11. R.color.notification_material_background_low_priority_color);
  12. recoveredBuilder.setBackgroundColorHint(backgroundColor);
  13. }
  14. if (notification.isMediaNotification()) {
  15. MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
  16. packageContext);
  17. processor.setIsLowPriority(mIsLowPriority);
  18. processor.processNotification(notification, recoveredBuilder);
  19. }
  20. return createRemoteViews(mReInflateFlags,
  21. recoveredBuilder, mIsLowPriority, mIsChildInGroup,
  22. mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
  23. packageContext);
  24. } catch (Exception e) {
  25. mError = e;
  26. return null;
  27. }
  28. }
  29. @Override
  30. protected void onPostExecute(InflationProgress result) {
  31. if (mError == null) {
  32. mCancellationSignal = apply(result, mReInflateFlags, mRow, mRedactAmbient,
  33. mRemoteViewClickHandler, this);
  34. } else {
  35. handleError(mError);
  36. }
  37. }

从msbn中获取 notifaction,判断是否是媒体类型的通知,进行对应的主题背景色修改,通过传递的优先级设置通知背景色,继续看核心方法 createRemoteViews()

  1. private static InflationProgress createRemoteViews(int reInflateFlags,
  2. Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
  3. boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
  4. Context packageContext) {
  5. InflationProgress result = new InflationProgress();
  6. isLowPriority = isLowPriority && !isChildInGroup;
  7. if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
  8. result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
  9. }
  10. if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
  11. result.newExpandedView = createExpandedView(builder, isLowPriority);
  12. }
  13. if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
  14. result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
  15. }
  16. if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
  17. result.newPublicView = builder.makePublicContentView();
  18. }
  19. if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
  20. result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification()
  21. : builder.makeAmbientNotification();
  22. }
  23. result.packageContext = packageContext;
  24. return result;
  25. }

这里就是创建各种布局 CONTENT_VIEW、EXPANDED_VIEW、HEADS_UP_VIEW、PUBLIC_VIEW、AMBIENT_VIEW,

然后回到 AsyncInflationTask 的 onPostExecute()中执行 apply(),代码太多就不贴了, SystemUI部分的通知流程分析技术,欢迎留言讨论。

statusBar左边区域(notification_icon_area)看完了,接下来看下右边的系统图标区域(system_icon_area)

Android8.1 SystemUI源码分析之 电池时钟刷新

从根源上屏蔽Notification

frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

注释如下代码

  1. mHandler.post(new EnqueueNotificationRunnable(userId, r))

Android8.1 SystemUI源码分析之 Notification流程的更多相关文章

  1. Android8.1 SystemUI源码分析之 电池时钟刷新

    SystemUI源码分析相关文章 Android8.1 SystemUI源码分析之 Notification流程 分析之前再贴一下 StatusBar 相关类图 电池图标刷新 从上篇的分析得到电池图标 ...

  2. Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新

    SystemUI系列文章 Android8.1 MTK平台 SystemUI源码分析之 Notification流程 Android8.1 MTK平台 SystemUI源码分析之 电池时钟刷新 And ...

  3. nodejs的Express框架源码分析、工作流程分析

    nodejs的Express框架源码分析.工作流程分析 1.Express的编写流程 2.Express关键api的使用及其作用分析 app.use(middleware); connect pack ...

  4. openVswitch(OVS)源码分析之工作流程(哈希桶结构体的解释)

    这篇blog是专门解决前篇openVswitch(OVS)源码分析之工作流程(哈希桶结构体的疑惑)中提到的哈希桶结构flex_array结构体成员变量含义的问题. 引用下前篇blog中分析讨论得到的f ...

  5. Okhttp源码分析--基本使用流程分析

    Okhttp源码分析--基本使用流程分析 一. 使用 同步请求 OkHttpClient okHttpClient=new OkHttpClient(); Request request=new Re ...

  6. MyBatis源码分析-MyBatis初始化流程

    MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...

  7. Duilib源码分析(六)整体流程

    在<Duilib源码分析(一)整体框架>.<Duilib源码分析(二)控件构造器—CDialogBuilder>以及<Duilib源码分析(三)XML解析器—CMarku ...

  8. 安卓Monkey源码分析之运行流程

    在<MonkeyRunner源码分析之与Android设备通讯方式>中,我们谈及到MonkeyRunner控制目标android设备有多种方法,其中之一就是在目标机器启动一个monkey服 ...

  9. SpringMVC源码分析-400异常处理流程及解决方法

    本文涉及SpringMVC异常处理体系源码分析,SpringMVC异常处理相关类的设计模式,实际工作中异常处理的实践. 问题场景 假设我们的SpringMVC应用中有如下控制器: 代码示例-1 @Re ...

随机推荐

  1. 借 redis cluster 集群,聊一聊集群中数据分布算法

    Redis Cluster 集群中涉及到了数据分布问题,因为 redis cluster 是多 master 的结构,每个 master 都是可以提供存储服务的,这就会涉及到数据分布的问题,在新的 r ...

  2. 用Java编程语言对一个无序整形数组进行排序(冒泡排序,选择排序,插入排序)

    public static void main(String[] args) { /** * 冒泡排序 * 思路:每个轮次都让第一个数和其后所有的数进行轮比较,如果这轮的第一个数大则和其下一个数交换位 ...

  3. CF595 Div31249A,1249B1,1249B2,1249C1,1249C2,1249D1,1249D2,1249E,1249F)题解

    A:水题,先排序,有相连的输出2,否则输出1. #include<bits/stdc++.h> using namespace std; #define pii pair<int,i ...

  4. numpy的基本API(三)——索引

    numpy的基本索引API iwehdio的博客园:https://www.cnblogs.com/iwehdio/ 1.单个元素的索引 对于一维数组,索引方式与内置的List相同.正索引从0开始,负 ...

  5. 智和网管平台国产化AIOps智能运维 建立自主可控网络安全体系

    没有网络安全就没有国家安全,中国作为一个崛起中的大国,网络安全至关重要.新一届中央高度重视信息安全自主可控的发展,Gartner研究报告表明,2019年中国三分之二的数据中心.IT基础设施支出流向中国 ...

  6. unittest自动化测试框架

    目录 框架的概念 Unittest单元测试框架 常用的assert语句 unittest创建测试代码的方式: unittest构建测试套件(测试用例集合): unittest忽略测试用例: 运行测试集 ...

  7. Django ContentType 的使用

    引入 一切优化,最终都是关于需求的优化.本文介绍需求确定之后的数据库表结构设计优化. 程序员应该都知道,编程是数据结构和算法的结合.所谓数据就是用户需要访问和操作的资源,比如购物类App里面的商品,图 ...

  8. Oracle用户、角色、权限

    一.Oracle权限 系统权限:系统规定用户使用数据库的权限.(系统权限是对用户而言) 对象权限:某种权限用户对其它用户的表或视图的存取权限.(是针对表或视图而言的) 1.Oracle系统权限 ​ o ...

  9. js反爬学习(一)谷歌镜像

    1. url:https://ac.scmor.com/ 2. target:如下链接 3. 过程分析: 3.1 打开chrome调试,进行元素分析.随便定位一个“现在访问” 3.2 链接不是直接挂在 ...

  10. .Net配置Ajax跨域访问

    1.在web.config文件中的 system.webServer 节点下 增加如下配置 <httpProtocol> <customHeaders> <add nam ...