代码流程

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

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

FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setBouncerShowing(mBouncerShowing);
setAreThereNotifications();
checkBarModes();
/// M: add for plmn display feature @{
attachPlmnPlugin();
///@}
}).getFragmentManager()
.beginTransaction()
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit();

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

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

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

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

1、notification_lights_out---ImageView默认gone

2、status_bar_contents--LinearLayout

	notification_icon_area--FrameLayout

	system_icon_area--LinearLayout

			system_icons.xml(蓝牙、wifi、VPN、网卡、SIM卡信号、飞行模式等) 电池

			clock--Clock.java 

3、emergency_cryptkeeper_text--ViewStub(延迟加载 紧急电话文字)

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

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

public void initNotificationIconArea(NotificationIconAreaController
notificationIconAreaController) {
ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
mNotificationIconAreaInner =
notificationIconAreaController.getNotificationInnerAreaView();
if (mNotificationIconAreaInner.getParent() != null) {
((ViewGroup) mNotificationIconAreaInner.getParent())
.removeView(mNotificationIconAreaInner);
}
notificationIconArea.addView(mNotificationIconAreaInner);
// Default to showing until we know otherwise.
showNotificationIconArea(false);
}

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

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

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

 public void hideSystemIconArea(boolean animate) {
animateHide(mSystemIconArea, animate);
} public void showSystemIconArea(boolean animate) {
animateShow(mSystemIconArea, animate);
} public void hideNotificationIconArea(boolean animate) {
animateHide(mNotificationIconAreaInner, animate);
} public void showNotificationIconArea(boolean animate) {
animateShow(mNotificationIconAreaInner, animate);
}

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

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

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

public View getNotificationInnerAreaView() {
return mNotificationIconArea;
} protected void initializeNotificationAreaViews(Context context) {
reloadDimens(context); LayoutInflater layoutInflater = LayoutInflater.from(context);
mNotificationIconArea = inflateIconArea(layoutInflater);
mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById(
R.id.notificationIcons); mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();
} protected View inflateIconArea(LayoutInflater inflater) {
return inflater.inflate(R.layout.notification_icon_area, null);
} //notification_icon_area.xml
<com.android.keyguard.AlphaOptimizedLinearLayout
xmlns: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" >
<com.android.systemui.statusbar.phone.NotificationIconContainer
android:id="@+id/notificationIcons"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:gravity="center_vertical"
android:orientation="horizontal"/>
</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监听通知消息

try {
mNotificationListener.registerAsSystemService(mContext,
new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
UserHandle.USER_ALL);
} catch (RemoteException e) {
Log.e(TAG, "Unable to register notification listener", e); } private final NotificationListenerWithPlugins mNotificationListener =
new NotificationListenerWithPlugins() {
@Override
public void onListenerConnected() {
...... services成功启动,获取当前处于活动状态的通知(没被移除的通知),添加到通知栏,此处应该是重启后重新加载
} @Override
public void onNotificationPosted(final StatusBarNotification sbn,
final RankingMap rankingMap) {
...... 收到通知消息,添加或者修改
if (isUpdate) {
updateNotification(sbn, rankingMap);
} else {
addNotification(sbn, rankingMap);
}
} @Override
public void onNotificationRemoved(StatusBarNotification sbn,
final RankingMap rankingMap) {
...... 移除通知消息
if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
final String key = sbn.getKey();
mHandler.post(() -> removeNotification(key, rankingMap));
}
} @Override
public void onNotificationRankingUpdate(final RankingMap rankingMap) {
..... 通知的排序优先级改变,修改通知位置
if (rankingMap != null) {
RankingMap r = onPluginRankingUpdate(rankingMap);
mHandler.post(() -> updateNotificationRanking(r));
}
} };

继续来看下 addNotification()方法

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

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

protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
throws InflationException {
if (DEBUG) {
Log.d(TAG, "createNotificationViews(notification=" + sbn);
}
NotificationData.Entry entry = new NotificationData.Entry(sbn);
Dependency.get(LeakDetector.class).trackInstance(entry);
entry.createIcons(mContext, sbn);
// Construct the expanded view.
inflateViews(entry, mStackScroller);
return entry;
} protected void inflateViews(Entry entry, ViewGroup parent) {
PackageManager pmUser = getPackageManagerForUser(mContext,
entry.notification.getUser().getIdentifier()); final StatusBarNotification sbn = entry.notification;
if (entry.row != null) {
entry.reset();
updateNotification(entry, pmUser, sbn, entry.row);
} else {
new RowInflaterTask().inflate(mContext, parent, entry,
row -> {
bindRow(entry, pmUser, sbn, row);
updateNotification(entry, pmUser, sbn, row);
});
} }

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

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

public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
RowInflationFinishedListener listener) {
mListener = listener;
AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
mEntry = entry;
entry.setInflationTask(this);
inflater.inflate(R.layout.status_bar_notification_row, parent, this);
}

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

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

private void updateNotification(Entry entry, PackageManager pmUser,
StatusBarNotification sbn, ExpandableNotificationRow row) {
..... entry.row = row;
entry.row.setOnActivatedListener(this); boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
mNotificationData.getImportance(sbn.getKey()));
boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded;
row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
row.updateNotification(entry);
}

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

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

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

new AsyncInflationTask().execute();

@Override
protected InflationProgress doInBackground(Void... params) {
try {
final Notification.Builder recoveredBuilder
= Notification.Builder.recoverBuilder(mContext,
mSbn.getNotification());
Context packageContext = mSbn.getPackageContext(mContext);
Notification notification = mSbn.getNotification();
if (mIsLowPriority) {
int backgroundColor = mContext.getColor(
R.color.notification_material_background_low_priority_color);
recoveredBuilder.setBackgroundColorHint(backgroundColor);
}
if (notification.isMediaNotification()) {
MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
packageContext);
processor.setIsLowPriority(mIsLowPriority);
processor.processNotification(notification, recoveredBuilder);
}
return createRemoteViews(mReInflateFlags,
recoveredBuilder, mIsLowPriority, mIsChildInGroup,
mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
packageContext);
} catch (Exception e) {
mError = e;
return null;
}
} @Override
protected void onPostExecute(InflationProgress result) {
if (mError == null) {
mCancellationSignal = apply(result, mReInflateFlags, mRow, mRedactAmbient,
mRemoteViewClickHandler, this);
} else {
handleError(mError);
}
}

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

private static InflationProgress createRemoteViews(int reInflateFlags,
Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
Context packageContext) {
InflationProgress result = new InflationProgress();
isLowPriority = isLowPriority && !isChildInGroup;
if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
} if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
result.newExpandedView = createExpandedView(builder, isLowPriority);
} if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
} if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
result.newPublicView = builder.makePublicContentView();
} if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification()
: builder.makeAmbientNotification();
}
result.packageContext = packageContext;
return result;
}

这里就是创建各种布局 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

注释如下代码

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. luogu P2650 弹幕考察

    题意简化:求某个区间在一组区间中覆盖的数量 对于这个问题,我们很容易想到线段树,或者树状数组,但是maxlongint不能让我们这么做 30分思路: 对于m个区间,枚举n个区间判断与它是否重合 但是O ...

  2. Xcode中.a文件引起的错误

    一.     TARGETS -> Build Settings-> Search Paths下 1.  Library Search Paths 删除不存在的路径,保留.a文件的路径(此 ...

  3. Java修炼——四种方式解析XML_JDOM

    四种方式解析XML:DOM     JDOM    DOM4J    SAX JDOM使用前需要上传jar包. 先写一个XML栗子: <?xml version="1.0" ...

  4. [TimLinux] Python nonlocal和global的作用

    1. 执行代码 以下实例都是通过执行以下代码,需要把以下执行代码放在后面实例代码的后面. a = outer_func()print("call a()") a() a() a() ...

  5. hdu3999 The order of a Tree

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3999 题意:给一序列,按该序列插入二叉树,给出字典序最小的插入方法建相同的一棵树出来.即求二叉树的先序 ...

  6. 使用java语言实现八皇后问题

    八皇后问题,在一个8X8的棋盘中,放置八个棋子,每个棋子的上下左右,左上左下,右上右下方向上不得有其他棋子.正确答案为92中,接下来用java语言实现. 解: package eightQuen; / ...

  7. 【Webpack】373- 一看就懂之 webpack 高级配置与优化

    本文原载于 SegmentFault 社区专栏 前海拾贝 作者:JS_Even_JS 一.打包多页面应用 所谓打包多页面,就是同时打包出多个 html 页面,打包多页面也是使用 html-webpac ...

  8. 【Spring Session】和 Redis 结合实现 Session 共享

    [Spring Session]和 Redis 结合实现 Session 共享 参考官方文档 HttpSession with Redis Guide https://docs.spring.io/s ...

  9. 3年java开发面试BAT,你必须彻底搞定Maven!

    前言 现在的Java项目中,Maven随处可见. Maven的仓库管理.依赖管理.继承和聚合等特性为项目的构建提供了一整套完善的解决方案,如果你搞不懂Maven,那么一个多模块的项目足以让你头疼,依赖 ...

  10. 如何用Jmeter做接口测试

    Jmeter介绍&测试准备: Jmeter介绍:Jmeter是软件行业里面比较常用的接口.性能测试工具,下面介绍下如何用Jmeter做接口测试以及如何用它连接MySQL数据库. 前期准备:测试 ...