Android8.1 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默认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源码分析之 电池时钟刷新
-
MapReduce之Job提交流程源码和切片源码分析
-
啃下MyBatis源码 - MyBatis核心流程三大阶段之代理阶段(binding模块分析)
-
Hadoop源码学习笔记之NameNode启动流程分析一:源码环境搭建和项目模块及NameNode结构简单介绍
-
Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新
-
Android8.1 SystemUI源码分析之 电池时钟刷新
-
MapReduce之Job提交流程源码和切片源码分析
-
【三】Spring 源码分析之启动主流程---SpringApplication的prepareContext方法
-
Android8.1 SystemUI源码分析之 Notification流程
-
Hadoop源码学习笔记之NameNode启动流程分析一:源码环境搭建和项目模块及NameNode结构简单介绍