王红元高级讲师
专注移动开发领域,多年iOS开发和教学经验,曾经带领团队开发出众多款 优秀APP作品。
国内知名视频聊天软件 < 新浪秀场 > 就出自王老师之手,王老师全 面负责 < 新浪秀场 > iOS客户端的设计和开发,并参与了 < 新浪秀场 > Android、Windows 、web端
开发。同时王老师还曾兼职担任过多家公司技术顾问,帮助其解决开发中的 重点难题。
除此之外,王老师还主导开发了< 智慧城市 > < 天翼看交通 > < 公交wifi > 等热门APP。王老师开发过的作品涉及旅游、交通、社交、视频等方面,对即时聊天、流媒体等技术有深入研究。
王老师不但开发经验丰富,授课风趣幽默,且激情四射。擅长以各种生活中 的案例出发,帮助学生理解消化学习中的各种难点。另外,王老师教学总是以学生角度思考讲解,备受学生喜爱。
现在学习Java还有前途吗?
在互联网行业,Java工程师是一个技术含量非常高的岗位,支撑了互联网行 业的半壁江山,全世界有一千万Java程序员,目前还在有更多的计算机爱好者向Java的大门奔来。Java发展了20多年,关于Java的悲观论调也不时出现 ,现在学Java还有前途吗?是否已经过了红利期了呢?
Java作为所有编程语言中热门技术,可以说它无处不在,目前全球有着数十 亿的设备正在运行着Java,很多服务器程序都是用Java编写,用以处理每天超过数以千万的数据。
无论是手机软件、手机Java游戏还是电脑软件,每一次购物到每一笔支付成 功,都离不开Java,越来越多的企业也正采用Java语言开发网站,而在所有程序员中,Java开发工程师就占据了20%的比例。
从国内排名靠前网站的主要开发语言,也可以看出Java在各大开发语言中的 地位。淘宝、搜狐、网易等一线互联网公司,都在使用Java开发语言。
这也不难理解,为什么Java现在这么火爆,吸引越来越多的人学习,根本原 因,还是因为企业对Java的认可和应用。
Java有没有前途,需要我们认清自己今后应该怎么走自己的路线,是走技术 ,还是走管理。走技术路线:从初级、中级、高级再到软件架构师。如果说走管理路线:项目经理、部门经理、技术总监。只有清楚了自己要走的路线 后,再往决定的那一方面去努力,学习。除此以外,还有一些人学了这一专业后,从事销售顾问、培训讲师、自己创业的都有,关键是自己要认识自己 ,自己更适何哪一条路。
所以,先认清“正确的结果”,根据正确的结果去设计你的过程。当一个人 具有明确的职业目标时,就会对有助于实现目标的蛛丝马迹都特别敏感,做事情相应也就会很有目的性,而不是稀里糊涂的。所以,就容易到达自己的 目的。如果没有职业目标,脚踩西瓜皮滑到哪里是哪里,学什么技术都没前途。
在未来的几年,Java工程师人才的需求还在不断的加大,由于人才的紧缺, 这个岗位相对于其它专业薪资待遇还是不错的,而且Java工程师的待遇是与工作经验直接挂勾的,当你有了丰富的经验以后,你在这个行业里就比较抢 手了,而且企业所出的薪酬也是相当高的,到时只有你选择他们了。
大数据核心知识
大数据核心知识
Hadoop基础
Hadoop1介绍
hadoop1架构
hadoop2架构(对比hadoop1)
hadoop2环境搭建
HDFS操作
yarn操作
Hadoop应用
Hive数据仓库
zookeeper系统服务
HBase非关系型数据库
Sqoop数据库抽取工具
Flume日志抽取工具
Spark基础
环境搭建
Spark平台介绍
RDD弹性分布式数据集
Scala编程
Spark应用
Spark-SQL组件
DataFrame组件
课程优势
1.真实的企业项目;
2.目前企业中应用广泛的技术路线;
3.部分Spark源码剖析,从源码层面提升问题解决能力。
4.从hadoop1到hadoop2机制原理详细解说;
5.生产环境hadoop集群调优经验;
6.企业真实项目实战;
本阶段学习目标
1.了解hadoop机制原理 ;
2.了解hadoop集群搭建过程;
3.了解Hdfs API使用以及mr编程模型;
4.了解hive、hbase、sqoop、flume等组件的使用方法;
5.Spark平台的优势以及Spark集群的搭建过程;
6.Scala程序设计基础;
7.Spark-SQL和DataFrame API详解。
本阶段学习效果
1.了解hadoop集群的搭建过程;
2.能够**mr和hive来实现简单的数据清洗的业务需求;
3.能够了解数据的抽取,转换,清洗,建模,入库过程;
4.掌握Spark集群的搭建;
5.掌握函数式编程思想,能够根据业务需求编写高质量的Scala程序;
6.掌握大规模离线数据的计算、分析能力。
SystemUI架构分析
>
SystemUI架构分析
SystemUI架构分析 前言 1SystemUI介绍 1SystemUI摘要 2什么是SystemUI 2SystemUI的启动过程 3SystemUI的SERVICES 1音量控制 11音量控制简介 12音量控制SERVICE的初始化 13控制音量过程 2RingtonePlayer 3电源管理 4任务管理 5存储通知 6锁屏 7通知栏 4总结
前言
本文描述Android系统中一个核心应用SystemUI,详细赘述SystemUI中几大模块功能的实现过程。由于作者水平有限,如发现本文中错误的地方,欢迎指正。
1、SystemUI介绍
1.1、SystemUI摘要
在Android系统中SystemUI是以应用的形式运行在Android系统当中,即编译SystemUI模块会生产APK文件,源代码路径在frameworks/base/packages/SystemUI/,安装路径system/PRiv-app/-SystemUI。
1.2、什么是SystemUI
在前文1.1章节中可知,SystemUI是一个普通的APK文件,即是一个普通的APP,但是,手机使用者看见的所有SystemUI的内容都不像是一个APP,为什么?既然是一个应用,就是完成特定的功能,SystemUI主要完成的功能有: (1)、Status bars (2)、Navigation bars (3)、Notification (4)、Lockscreen (5)、Quick settings (6)、Overview(recent task switcher) (7)VolumeUI
2、SystemUI的启动过程
正如1.2中所述,SystemUI任何内容都不像一个APP,自然它的启动也不像大多APP一样启动一个Activity。SystemUI顾名思义就是全局UI,必须在开机过程中完成启动,并不可退出。 回顾Android系统开机的过程,会创建server进程维护系统各种服务,当服务启动完成后调用systemReady方法(如果读者不了解这个过程自行学习Android Boot Flow),如下图:
继续跟踪startSystemUi()方法的实现如下图:
上图中可以看到**熟悉的startServiceAsUser()方法启动SystemUI中的SystemUIService。由APP的启动过程可知(如果读者不了解这个过程可自行学习),Android系统会给应用创建独立的进程,并实例化application对象,在SystemUI源码中的AndroidManifest.xml文件可以看到下图的配置:
上图中在application标签中指定了SystemUIApplication对象,因此在启动SystemUI应用时会创建SystemUIApplication对象并回调onCreate()方法,如下图:
onCreate()中注册ACTION_BOOT_COMPLETED广播,并调用mServices.onBootCompleted()方法,将在后面的内容中赘述该方法。应用成功启动后便可执行上文中提到的SystemUIService服务,并回调onCreate()方法,如下图:
上图中**获取应用的Application实例SystemUIApplication对象,调用startServiceIfNeeded(),如下图:
上图中读者可能会问:mService的实质是什么?回顾1.2章节中的内容,SystemUI主要分几大模块,即在SystemUI中每个模块是一个Service,这样一来,各个模块就非常独立。上图中用java映射机制把每个Service的对象实例化,并调用start()方法启动各个服务,start()方法的具体实现在分析各个服务时再赘述。先了解mService实质都包含哪些具体“对象”,如下图:
如上图所示,SystemUI需要启动的Service包括KeyguardViewMediator、Recent、VolumeUI、SystemBars、StorageNotification、PowerUI、RingtongPlayer共7个,每个service的具体实现和功能将在下文中描述。在图6中可以看到,实例化每个对象是向上转型为SystemUI对象,即图7中的所有service统一继承了SystemUI类,如下图:
SystemUI类提供start()、onConfigurationChanged()、dump()等重要方法,每个方法在各个service中实现不一样,下面将一一描述每个service在SystemUI类中的方法的实现。 至此,SystemUI的启动基本完成,从上文可知,SystemUI是系统中非常核心的应用,在Android系统开机过程中Server进程直接发起SystemUI启动,SystemUI也是固化程序,在保证系统正常运行发挥了非常重要的作用。
3、SystemUI的SERVICES
3.1、音量控制
3.1.1、音量控制简介
如图章节1.2中的VolumeUI所示,当用户操作音量键时,会弹出相应的UI显示,并可以设置音量大小和情景模式。VolumeUI的主要代码在SystemUI/volume下。在不同模式下,音量键触发的UI显示样式不一样,分别是通话、铃声(通知)、音乐、闹铃、蓝牙输出等,如下图 9-13
3.1.2、音量控制SERVICE的初始化
在第二章节中SystemUI的启动过程提到,SystemUI的所有Service**SystemUI类的start()方法启动,并且**图7可以知道,volume service的VolumeUI继承了SystemUI类,所以start()实质是执行VolumeUI中的方法,如下图:
如上图中的代码,首先读取VolumeUI的开关,如果mEnabled为true,则调用initPanel()方法实例化UI等控件元素(如图15),实例化VolumeController控制器(如图17),调用putComponent()保存对象实例,调用updateController()设置控制器(如图18)。
上图中主要是new一个VolumePanel对象,VolumePanel是Handler的子类,且又是VolumeUI的Pannel,因此,VolumePanel负责绘制VolumeUI的内容和控制VolumeUI的显示。先看看VolumeUI的创建过程:
从上图中的代码可见,VolumeUI是以Dialog的形式显示UI,VolumePanel的实例化过程创建Dialog实例和初始化ZenModePanel,到此VolumePanel将会待命。上文中提到VolumePanel同时是Handler的子类,一旦VolumePanel收到相关的Message时,将会处理UI的显示和关闭。
上图是VolumeController的实现代码,主要实现对VolumeUI的Panel的控制,例如上图中的volumeChanged()控制Panel的显示和变化,dismissNow()控制Panel的关闭。那么VolumeController是被谁管控呢?如下图:
图中可以看到先**设置Provider读取是否允许systemui控制volume,如果允许,则设置**AudioManager的实例设置VolumeController到AudioService(读者如果不了解这个过程,可以自行阅读Android Audio策略)。至此,VolumeUI的初始化全部完成。 **本节的学习,VolumeUI的架构如下图:
3.1.3、控制音量过程
当SystemUI的VolumeUI当前不是活动窗口时,一般情况下,音量的设置是**音量键进行操作,当用户操作音量键时,如果用户不拦截音量键事件,那么默认音量键的事件将会在Window中被消化,Window将捕获到的音量键事件**Binder机制将音量变化信息传送到MediasessionService,MediaSessionService同样**Binder机制接着传送到AudioService,**后AudioService也同样**Binder机制把信息传送给SystemUI(VolumeUI),VolumeUI将会作出相应的变化。下面将详细了解这个过程: 当手机设备当前活动窗口在Laucher桌面,Laucher没有对音量键事件作拦截操作,音量键事件将会在PhoneWindow中被消化。在Android的单次点击事件中,分down和up两种事件,分别被分发到PhoneWindow的onKeyUp()和onKeyDown()方法中,如下图20-21:
图20是消化down事件,图21是消化up事件,但音量键还有上音量键( )和下音量键(-),从这两张图可以看到,KEYCODE_VOLUME_UP没有作任何处理,上音量键( )的事件会在下音量键中消化(-), 在down和up事件中都是调用sendAdjustVolumeBy()同一个方法,传递三个参数,**个参数是指定音量类型,mVolumeControlStream为默认值,取值Integer.MIN_VALUE,图20和图21相同,第二个是delta,音量控制类型,即增加或减少,图20传递direction,图21传递0,direction取值1或-1,即1:增加、0:不变、-1:减少,第三个参数是flags,控制VolumeUI显示,每个参数具体的控制的实现代码将在下文中描述。继续跟踪流程,Laucher进程**Binder机制把信息传送到MediaSessionService,如下图:
上图中有获取MediaSessionRecord的对象来控制音量,这里的session变量的值是null,如果读者对此感兴趣可自行阅读相关资料。继续看dispatchAjustVolumeLocked()方法:
图23中可以知道参数只是多了一个packageName,其它的都是图20或图21中的参数值。接着往下看:
在图20或图21中有描述suggestedStreamType的值是Integer.MIN_VALUE,在上图中**getActiveStreamType()方法对值进行转换,变成streamType,它的取值可能是0到10,分别控制不同类型的音量,如3.1.1章节中所以,本例子streamType的值是2,即调整的是铃声(通知)的音量。接着又调用了adjustStreamVolume()方法,如下:
adjustStreamVolume()方法对direction和streamType的合法进行校验,direction取值-1到1,streamType取值0到10。之后**mStreamState获取oldIndex、newIndex和index值,其中oldindex和index作为sendVolumeUpdate()方法的参数,将会影响音量变化的广播和AudioProfile,关于AudioProfile读者感兴趣可以自行学习。图中还可以看到这里还设置了HDMI接口输出。继续看sendVolumeUpdate()方法:
在sendVolumeUpdate()方法中处理了几个事件,一个postVolumeChanged()方法,**终通知SystemUI,后面赘述。接着发送注册到AudioManager.VOLUME_CHANGED_ACTION的action的广播,通知音量改变并携带音量大小的原值oldIndex和新值index,**后通知AudioProfile。继续看postVolumeChanged()方法:
这里**mControlle对象调用volumeChanged(),mController实质是一个什么类的实例,回顾3.1.2章节中的图18,updateController()方法设置了VolumeController的实例,因此mController正是VolumeController在AudioService中的句柄,**Binder机制,把音量变化的信息从AudioService传输到SystemUI进程。转移到SystemUI,如下图:
从AudioService回调到volumeChanged()方法,接着调用mPanel的postVolumeChanged()方法,mPanel在前文3.1.2章节的图15中有描述,是VolumePanel的实例,前文中提到,VolumePanel是Handler的子类,也是VolumeUI的Panel,下面结合代码分析VolumePanel的具体功能:
上文提到VolumePanel是Handler的子类,图29中VolumePanel将发送MSG_VOLUME_CHANGED的Message到自身持有的线程,接着看MSG_VOLUME_CHANGED的代码:
上图中直接又调用了onShowVolumeChanged()方法,顾名思义是显示音量变化的UI,后面接着继续调用resetTimeout()方法,先跟踪onShowVolumeChanged()方法:
上图中首先调用getStreamVolume()方法获取对应streamType当前的音量值,**StreamControl匹配streamType的UI,StreamControl是一个容器,在VolumeUI的初始化时被实例化,装载不同streamType的UI配置,并保存到mStreamControls数组对象中,因此streamType的值确定了StreamControl的类型,StreamControl确定了Dialog显示的UI类型。确定StreamControl的类型后,调用updateSliderProgress()方法更新界面控件,**后调用mDialog.show()方法绘制界面。至此,从按下音量键到调用mDialog.show(),设备对点击事件作出相应,并显示相应的UI到界面上。 回顾图31,resetTimeout()方法的实现如下:
这里会延时发送一个空消息到VolumePanel,what为MSG_TIMEOUT,mTimeoutDelay的值为3000,跟踪MSG_TIMEOUT的处理过程:
很简单,实现的功能就是在VolumeUI显示后,延时3秒自动把VolumeUI关闭。 到此SystemUI的VolumeUI service分析完毕,VolumeUI的流程简单清晰,代码简洁,阅读方便。回顾VolumeUI整个控制流程,可用下图总结:
上图中PhoneWindow是当前活动窗口的进程的PhoneWindow实例捕获音量键事件,当前活动窗口的可以**自身的PhoneWindow对象实例设置streamType,即控制音量键事件触发的VolumeUI类型。当应用程序拦截音量键事件,那么PhoneWindow将无法捕获到音量键事件,此时音量键事件将不遵行上图的流程。如果当前活动窗口时SystemUI,则直接由SystemUI所在进程的活动窗口的PhoneWindow对象实例获取到音量键事件,这时音量键事件的转发和处理和在其它进程(如Laucher)中略有不同,读者感兴趣可以自行比较,本文不再论述。
3.2、RingtonePlayer
RingtonePlayer在SystemUI中扮演播放者的角色,代码在SystemUI/media/目录下,RingtonePlayer的代码很少,功能很简单,同VolumeUI,RingtonePlayer继承SystemUI类,SystemUI应用启动时调用start()方法:
Start()方法很简洁,**AudioService的句柄mAudioService的setRingtonePlayer()方法设置mCallback,mCallback实质是IRingtonePlayer对象的实例,如下图:
IRingtonePlayer有play()、stop()等方法,分别是播放音乐和停止音乐。关于RingtonePlayer的作用,读者可以参考下图:
SystemUI启动后**Binder机制把IRingtonePlayer的句柄设置到AudioServie,其它应用便可**AudioService获取IRingtonePlayer的句柄,在**Binder机制操作RingtonePlayerService去播放声音文件。如果读者感兴趣可自行深入学习Ringtone策略和架构。
3.3、电源管理
电源管理即PowerUI,负责监控电源的变化和通知,源码路径在SystemUI/power。同样PowerUI继承SystemUI类,start()方法如下:
上图可以知道,start()方法注册鉴定Setting.Global.LOW_POWER_MODE_TRIGGER_LEVEL的变化,接着调用mReceiver.init()方法,如下:
图 39注册一个action包含Intent.ACTION_BATTERY_CHANGED的广播接收器,监听电量的变化。然后调用updateSaverMode()方法:
如上图,updateSaverMode()的**终实现也是很简单,根据不同状态显示不同的notification。这就是PowerUI主要的功能,关于PowerUI本文就描述到这里,如果读者对PowerUI的更多细节感兴趣,可参考相关资料。
3.4、任务管理
任务管理即Recents,代码路径在SystemUI/recents。Recents是手机设备基本和常用的功能,主要功能表现为:展示所有任务,可以进行任务切换,可以进行任务的清除。同样Recents是SystemUI类的子类,同样启动时从start()方法开始启动。
Recents的start()方法很简洁,主要是sAlternateRecents.onStart()方法,sAlternateRecent是AlternateRecentsComponent的实例,AlternateRecentsComponent作为一个组件服务,担负着管理Recents的变化过程,AlternateRecentsComponent是单例设计模式,但一般sAlternateRecent只创建一次,过程如下:
图 42**getRecentsComponent()方法new一个AlternateRecentsComponent的对象实例,forceInitialize值是false,AlternateRecentsComponent实例化是会执行图43中的RecentsTaskLoader.initialize()方法实例化RecentsTaskLoader,RecentsTaskLoader是Recents读取器。接着又new一个SystemServicesProxy的实例,mSystemServicesProxy具有ActivityManage和PackageManage的功能,在读取Recents发挥了很重要的作用。回头看RecentsTaskLoader.initialize()的方法:
在RecentsTaskLoader.initialize()的方法中,除了实例化RecentsTaskLoader自己的同时,在RecentsTaskLoader的构造函数中同时实例化比较重要的两个变量,一个是TaskResourceLoadQueue,Recents资源队列,另外一个是TaskResourceLoader,Recents的资源读取器。下面根据SystemUI(Recents)的启动过程了解这些类的作用。 在大多设备当中,**长按HOME键打开任务管理器,HOME长按事件触发由输入输出事件派发者派发到系统进程(system_process)后被WindowManagerService拦截派发到PhoneWindowManager,如图:
上图中handleLongPressOnHome()将事件进一步发送到StatusBarManagerService,如图:
StatusBarManagerService**CommandQueue发送到SystemUI进程,关于StatusBarManagerService和CommandQueue在StatusBar service中论述。**后发送到AlternateRecentsComponent,如图:
到图47,事件经过了几个进程,终于到准备启动任务管理器的界面了,topTask是ActivityManager.RunningTaskInfo的实例,即正在系统运行的Activity,第二参数值为true。RecentsActivity启动回调onCreate()方法:
onCreate()方法完成了实例化RecentsTaskLoader、SystemServicesProxy、RecentsConfiguration,在前面的内容已提到,RecentsTaskLoader.initialize()方法同时实例化TaskResourceLoader和TaskResourceLoadQueue,然后注册一个监听Intent.ACTION_SCREEN_OFF action的广播接收器,即当屏幕熄灭后关闭RecentsActivity。RecentsActivity还复写了onStart()方法:
上图AlternateRecentsComponent.notifyVisibilityChanged(this, ssp, true)通知StatusBar RecentsActivity已经起来,StatusBar需要配合做相应的调整,之后调用updateRecentsTasks(),由于篇幅问题,下文将重点描述Tasks的管理,不再跟随代码的执行流程。 RecentsTasks的管理过程主要包括读取task,读取task资源,显示在RecentsActivity上,清除task。主要包括下面这些类: SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.ActivityInfoHandle SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader. TaskResourceLoadQueue SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader. TaskResourceLoader SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.Options RecentsTaskLoader是一个管理者,RecentsTaskLoader持有TaskResourceLoader,TaskResourceLoadQueue等实例,可以创建RecentsTaskLoadPlan实例,所以RecentsTaskLoader实质并没有去取读取task和task资源,只是负责发起读取task任务。ActivityInfoHandle 唯一持有一个AcitivityInfo的对象,保存Activity的信息,Activity的信息会在RecentsTaskLoadPlan中被查询。TaskResourceLoader 负责task的资源读取,和RecentsTaskLoader一样,它是一个管理者,实质实现读取task资源的是TaskResourceLoadQueue 。RecentsTaskLoadPlan对象实例也是被RecentsTaskLoader持有,实质上RecentsTaskLoadPlan是task读取的参与者和实现者,Options 是RecentsTaskLoadPlan的内部类,主要功能是控制RecentsTaskLoadPlan的读取task的条件。RecentsTaskLoadPlan读取task主要分两个阶段,**个个阶段是 上图中loader是RecentsTaskLoader的实例,调用preloadTasks()方法,即task读取的**阶段,plan是RecentsTaskLoadPlan的实例,第二个参数mConfig.launchedFromHome是boolean值,作用是isTopTaskHome,在启动RecentsActivity之前被赋值。这个阶段主要完成对task的读取。第二个阶段是: 上图中loadTasks()方法就是启动RecentsTaskLoadPlan的第二个阶段,同上图50,loader是RecentsTaskLoader的实例,**个参数this是Context的实例,plan是RecentsTaskLoadPlan的实例,loadOpts是Options的实例,loadOpts在这里的作用是控制RecentsTaskLoadPlan读取task的规则,第二个阶段必须基于**个阶段,也就是说在调用loadTasks()方法前,必须先执行preloadTasks()方法,loadTasks()基于preloadTasks()中读取到的task,从task中读取task的资源。 这两个阶段紧密连连,有先有后,完成不同的功能,下文将具体描述这两个阶段的过程。Loader. preloadTasks()实质调用RecentsTaskLoadPlan的如下方法 在这里会实例化一个TaskStack的对象实例,TaskStack封装了包含List类型的类,TaskStack包含两种类型的List,一种是存储所有的task,一种是filter task list,同时提供一个TaskFilter工具类接口,过滤主要**PakcageName作为匹配。然后调用preloadRawTasks()方法实例化mRawTasks,mRawTasks是一个ListActivityManager.RecentTaskInfo>实例,mRawTasks存储了RecentTaskInfo类型的实例,所以mRawTasks是读取task的关键,看mRawTasks的实例化过程 上图中mSystemServicesProxy是SystemServicesProxy的实例,调用图中的方法,再往下看 上图中mAm是ActivityManager的实例,getRecentTasksForUser()方法实质是**binder调用了远程的ActivityManagerService的方法,关于getRecentTasksForUser()这个方法在ActivityManagerService中的实现,读者可以自行阅读。在这里得到一个ListActivityManager.RecentTaskInfo>的对象实例tasks,RecentTaskInfo实质包含什么数据呢?如图 上图中是RecentTaskInfo的变量,得到这些数据,回到前面图52的地方,看下图
上图中获取到RecentTaskInfo后,把信息重新打包封装到TaskKey中,然后读取Activity的名字,Acitivity的图标,和thumbnail,然后把数据保存到TaskStack中。到此,**个阶段就完成了,在这个阶段完成了读取task的基本信息RecentTaskInfo,然后读取Activity的基本信息ActivityInfo,把数据保存到TaskStack中。 回到图50,**阶段完成后,接着就行图51中的第二阶段loader.loadTasks(),如下图
上图可以看到,这里获取icon和thumbnail都是在**个阶段就已经完成了动作,在这里在根据inRunningTask等条件再次刷新数据罢了。 到这里task和task资源读取完成,上上文中提到TaskResourceLoadQueue和TaskResourceLoader,这两个类在RecentsTaskLoadPlan的两个阶段都有使用,用于异步读取Activity Icon,TaskResourceLoadQueue会管理这些任务。 所有的Task数据到这里已准备就绪,接下下就是把数据显示在Activity上(屏幕上),对于数据怎样绑定到View,本文不再论述。
3.5、存储通知
存储通知即StorageNotification service,在SystemUI中主要完成对存储器变化的通知,即USB连接的变化,存储器的变化SystemUI发送相应的Notification。StorageNotification的启动如下:
StorageNotification的启动很简单,创建一个StorageNotification-EventListener的实例,把StorageNotificationEventListener**StorageManager注册到到MountService。当USB连接或存储器发生变化是远程回调到StorageNotificationEventListener的方法,如下图:
USB变化回调到onUsbMassStorageConnectionChangedAsync()进行处理,存储器发生变化回调到onStorageStateChangedAsync()处理,就是在发送notification通知USB和存储器的变化。这个模块本文就论述到这里,在SystemUI中StorageNotification结构清晰,功能简单,读者感兴趣可以自行了解。
3.6、锁屏
锁屏(Keyguard)service在SystemUI是一个比较特殊的模块,特殊在于SystemUI启动的service只是一个信息传递者,也就是KeyguardViewMediator,并没有做锁屏或解屏的实质操作。在这里,涉及到三个比较关键的类是: /SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java /SystemUI/src/com/android/systemui/keyguard/KeyguardService.java Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java KeyguardViewMediator和KeyguardService在源码中位于同一个GIT库(SystemUI)中,而KeyguardUpdateMonitor则位于KeyGuard库中。在KeyguardViewMediator的初始化中主要做了三件事,如图
实例化KeyguardUpdateMonitor的实例mUpdateMonitor,KeyguardUpdateMonitor负责更新已经锁屏界面上的内容,如时间,当然,KeyguardUpdateMonitor只是一个信息传递着,实际去刷新界面的是StatusBar模块。Keyguard模块通知StatusBar刷新解密那**KeyguardUpdateMonitorCallback这个类就行远程回调,该类的实例在StatusBar模块启动时**KeyguardService获取到IKeyguardService的远端实例,**IKeyguardService远程调用IKeyguardService的addStateMonitorCallback()方法实例化KeyguardUpdateMonitorCallback对象,如下图
SystemUI启动的Keyguard模块并没有真正的去操作锁屏界面,而是作为一个信息传递着把信息传递给StatusBar模块。这个模块本文就介绍到这里。
3.7、通知栏
通知栏(SystemBars)service是SystemUI中**重要的service,代码量**多,**复杂的,界面结构也复杂。根据前面的内容可知,启动SystemBars是**调用start()方法,如下图:
这里实质是回调到到SystemBars的onNoService()方法(这里涉及到安全设置启动的模式不一样,本文只讨论正常模式),**后是调用SystemBars的createStatusBarFromConfig()方法
上图可以看到,从string资源文件里面读取class name,**java的映射机制实例化对象,然后调用start()方法启动,class name的值如下图: 该配置文件在SystemUI/res/values/config.xml中。所以实质是PhoneStatusBar调用了start()方法。 SystemBars模块本文分两个阶段论述,**阶段是SystemBars的初始化过程,第二阶段是Notification的显示过程。**阶段主要涉及的类是: SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java PhoneStatusBar的父类是BaseStatusBar继承于SystemUI,上文提到,SystemBars调用PhoneStatusBar中的start()方法,下面跟随代码看看这个过程。
如上图,调用父类中的start()方法,即BaseStatsuBar中的start()方法。然后调用addNavigationBar()方法实例化导航条,这里不再赘述此功能。继续看BaseStatsuBar中的方法。
如上图BaseStatsuBar中的start()方法,实例化一个StatusBarIconList的对象,此处iconList对象是“空值”,然后**IStatusBarService的实例mBarService对象注册到StatusBarManager?Service。mCommandQueue是CommandQueue的实例,在Status?Bar-ManagerService的远程回调,实现StatusBarManagerService和SystemUI的通信。然后调用createAndAddWindows()方法,方法初始化status bar,notification,quick settings等的View控件。在这里,还需要注意NotificationListenerService的实例mNotificationListener的registerAsSystemService()方法,该方法主要实现StatusBarManagerService和SystemUI的notification的控制通道,也就是说,StatusBarManagerService收到notification变化时,**此通道通知SystemUI显示notification的变化。下文将介绍notification从StatusBarManagerService到SystemUI的过程。 一个APP需要显示notification首先需要实例化一个NotificationManager的对象,然后调用NotificationManager的方法notify()方法把创建好的Notification对象作为参数传进去。
上图中可以看到一个service的对象调用了enqueueNotification-WithTag()方法,该方法实质是远程调用NotificationManagerService中的enqueueNotificationWithTag()方法,该方法如下:
这里会把NotificationManager传递过来的Notification对象进行很多处理,比如变换成NotificationRecord,实质就是把Notification缓存下来。在上图的这个过程,还有一些其它的处理逻辑,在这里就不详细说明了,读者可以自行了解。上图中在代码的末尾调用了buzzBeepBlinkLocked()方法,该方法主要处理Notification的声音和震动的逻辑,本文也不再详述。接着看mListeners调用了notifyPostedLocked()方法,此方法**终会执行到如下图的代码
首先留意上图中final INotificationListener listener = (INotificationListener)info.service;这句代码,info.service返回一个INotificationListener的实例对象,该对象在上文中图66中的mNotificationListener.registerAsSystemService()方法进行设置,所以listener.onNotificationPosted()方法实质是远程回调SystemUI中的方法,如下图
如上图,这里又调用了Notification.Builder.rebuild()方法,该方法主要把**Binder机制传递过来的数据重新组装一些显示View所需要的数据,如notification的布局文件等等。重新组装notification数据后,调用NotificationListenerService.this.onNotificationPosted()方法,然后代码会执行到BaseStatusBar中的如下代码
上图中,代码运行又回到熟悉的BaseStatusBar.java类中,从APP调用NotificationManager的notify()方法到BaseStatusBar的addNotification()或updateNotification()方法,经历了一个复杂的过程,涉及的模块多,模块交互复杂。到此,本文就不再往下详情说明Notification到达SystemUI的处理过程了,读者感兴趣可自行阅读代码。
4、总结
本文详细描述了SystemUI中KeyguardViewMediator、Recents、VolumeUI、SystemBars、StorageNotification、PowerUI、RingtonePlayer 7个模块(SERVICE),其中SystemBars是SystemUI中起到中枢作用的一个模块,因为这个模块和其他模块交互**紧密,而且SystemUI中大多数UI的View都是在SystemBars中初始化和控制显示。不过,SystemUI的功能不止本文中说论述到的,SystemUI还有例如情景模式控制、流量警告和常用的屏幕截屏等功能,本文不再去说明它们,读者可自行去研究SystemUI中的每个功能。
相关推荐: