Android8.1 MTK平台 SystemUI源码分析之 Notification流程

2024-08-22 06:38

本文主要是介绍Android8.1 MTK平台 SystemUI源码分析之 Notification流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

流程图

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

代码流程

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默认gone2、status_bar_contents--LinearLayoutnotification_icon_area--FrameLayoutsystem_icon_area--LinearLayoutsystem_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(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);// 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.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"/>
</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() {@Overridepublic void onListenerConnected() {......  services成功启动,获取当前处于活动状态的通知(没被移除的通知),添加到通知栏,此处应该是重启后重新加载}@Overridepublic void onNotificationPosted(final StatusBarNotification sbn,final RankingMap rankingMap) {...... 收到通知消息,添加或者修改if (isUpdate) {updateNotification(sbn, rankingMap);} else {addNotification(sbn, rankingMap);}}@Overridepublic void onNotificationRemoved(StatusBarNotification sbn,final RankingMap rankingMap) {...... 移除通知消息if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {final String key = sbn.getKey();mHandler.post(() -> removeNotification(key, rankingMap));}}@Overridepublic 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 MTK平台 SystemUI源码分析之 Notification流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1095521

相关文章

Nginx分布式部署流程分析

《Nginx分布式部署流程分析》文章介绍Nginx在分布式部署中的反向代理和负载均衡作用,用于分发请求、减轻服务器压力及解决session共享问题,涵盖配置方法、策略及Java项目应用,并提及分布式事... 目录分布式部署NginxJava中的代理代理分为正向代理和反向代理正向代理反向代理Nginx应用场景

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的

Spring Boot分层架构详解之从Controller到Service再到Mapper的完整流程(用户管理系统为例)

《SpringBoot分层架构详解之从Controller到Service再到Mapper的完整流程(用户管理系统为例)》本文将以一个实际案例(用户管理系统)为例,详细解析SpringBoot中Co... 目录引言:为什么学习Spring Boot分层架构?第一部分:Spring Boot的整体架构1.1

nodejs打包作为公共包使用的完整流程

《nodejs打包作为公共包使用的完整流程》在Node.js项目中,打包和部署是发布应用的关键步骤,:本文主要介绍nodejs打包作为公共包使用的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言一、前置准备二、创建与编码三、一键构建四、本地“白嫖”测试(可选)五、发布公共包六、常见踩坑提醒

MyBatis Plus大数据量查询慢原因分析及解决

《MyBatisPlus大数据量查询慢原因分析及解决》大数据量查询慢常因全表扫描、分页不当、索引缺失、内存占用高及ORM开销,优化措施包括分页查询、流式读取、SQL优化、批处理、多数据源、结果集二次... 目录大数据量查询慢的常见原因优化方案高级方案配置调优监控与诊断总结大数据量查询慢的常见原因MyBAT

分析 Java Stream 的 peek使用实践与副作用处理方案

《分析JavaStream的peek使用实践与副作用处理方案》StreamAPI的peek操作是中间操作,用于观察元素但不终止流,其副作用风险包括线程安全、顺序混乱及性能问题,合理使用场景有限... 目录一、peek 操作的本质:有状态的中间操作二、副作用的定义与风险场景1. 并行流下的线程安全问题2. 顺

Ubuntu向多台主机批量传输文件的流程步骤

《Ubuntu向多台主机批量传输文件的流程步骤》:本文主要介绍在Ubuntu中批量传输文件到多台主机的方法,需确保主机互通、用户名密码统一及端口开放,通过安装sshpass工具,准备包含目标主机信... 目录Ubuntu 向多台主机批量传输文件1.安装 sshpass2.准备主机列表文件3.创建一个批处理脚

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

一个Java的main方法在JVM中的执行流程示例详解

《一个Java的main方法在JVM中的执行流程示例详解》main方法是Java程序的入口点,程序从这里开始执行,:本文主要介绍一个Java的main方法在JVM中执行流程的相关资料,文中通过代码... 目录第一阶段:加载 (Loading)第二阶段:链接 (Linking)第三阶段:初始化 (Initia