阅读Android 4.0源码也有一段时间了,这次是针对SystemUI的一个学习过程。本文只是对SystemUI分析的一个开始——启动流程的分析,网上有很多关于2.3的SystemUI的分析,可4.0与2.3的差别还是很大的,为了给自己留下笔记同时也方便大家学习和探讨,遂写此文,后续将有更多关于SystemUI的分析,敬请关注。

转载请注明出处:http://blog.csdn.net/yihongyuelan

1.初始SystemUI

什么是SystemUI?你或许会觉得这个问题很幼稚,界面上的布局UI显示?系统的UI?如果你是这么想的,那么就大错特错了。我们知道Android 4.0 ICS同时适用于Phone和Tablet(TV),因此,对于Phone来说SystemUI指的是:StatusBar(状态栏)、NavigationBar(导航栏)。而对于Tablet或者是TV来说SystemUI指的是:CombinedBar(包括了StatusBar和NavigationBar)。注:关于Android 4.0的UI介绍请参考这篇文章。

根据上面的介绍,我想大家应该知道SystemUI的具体作用了吧!也就是说我们的Phone的信号,蓝牙标志,Wifi标志等等这些状态显示标志都会在StatusBar上显示。当我们的设备开机后,首先需要给用户呈现的就是各种界面同时也包括了我们的SystemUI,因此对于整个Android系统来说,SystemUI都有举足轻重的作用,那接下来就来看看它的启动流程吧!

2.启动流程

这里只是单单的分析启动流程,实际上SystemUI启动过程中涉及到很多东西的调用,这里暂时不分支去介绍,后续会有相关文章的详细分析。那么对于这种分析我还是将自己的分析思路写出来,而不是直接展现已经分析好的结果,当然结果会在最后展示出来。这样做一方面有利于锻炼自己的分析能力,另一方面各位看官也可以找出分析中的利与弊从而更好的取舍。

首先来看看SystemUI的代码位置,路径:SourceCode/frameworks/base/packages/SystemUI;其次看看它的代码梗概:

图 2.1

在Android 4.0中,Google整合了Phone和Tablet(TV)的SystemUI,也就说可以根据设备的类型自动匹配相应的SystemUI。这一点是在Android 2.3中是没有的。那么接下来怎么分析呢?打开AndroidManifest.xml可以看到:

[html] view plaincopy
  1. <manifestxmlns:android="http://schemas.android.com/apk/res/android"
  2. package="com.android.systemui"
  3. coreApp="true"
  4. android:sharedUserId="android.uid.system"
  5. android:process="system"
  6. >
  7. <uses-permissionandroid:name="android.permission.STATUS_BAR_SERVICE"/>
  8. <uses-permissionandroid:name="android.permission.BLUETOOTH"/>
  9. <uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/>
  10. <uses-permissionandroid:name="android.permission.GET_TASKS"/>
  11. <uses-permissionandroid:name="android.permission.MANAGE_USB"/>
  12. <application
  13. android:persistent="true"
  14. android:allowClearUserData="false"
  15. android:allowBackup="false"
  16. android:hardwareAccelerated="true"
  17. android:label="@string/app_label"
  18. android:icon="@drawable/ic_launcher_settings">
  19. <!--Broadcastreceiverthatgetsthebroadcastatboottimeandstarts
  20. upeverythingelse.
  21. TODO:Shouldhaveanandroid:permissionattribute
  22. -->
  23. <serviceandroid:name="SystemUIService"
  24. android:exported="true"
  25. />
  26. <!--startedfromPhoneWindowManager
  27. TODO:Shouldhaveanandroid:permissionattribute-->
  28. <serviceandroid:name=".screenshot.TakeScreenshotService"
  29. android:process=":screenshot"
  30. android:exported="false"/>
  31. <serviceandroid:name=".LoadAverageService"
  32. android:exported="true"/>
  33. <serviceandroid:name=".ImageWallpaper"
  34. android:permission="android.permission.BIND_WALLPAPER"
  35. android:exported="true"/>
  36. <receiverandroid:name=".BootReceiver">
  37. <intent-filter>
  38. <actionandroid:name="android.intent.action.BOOT_COMPLETED"/>
  39. </intent-filter>
  40. </receiver>
  41. ......
  42. </application>
  43. </manifest>

根据以上代码我们可以发现这其中注册了很多Service,同时也包括了广播。但这里我们只关注SystemUIService,这才是本文的主旨啊。那么首先要找到SystemUIService是如何启动的。对于Service的启动,在我以前的博文中已有提到,这里就不多说了,不外乎startService(intent)和bindService(intent),它们都是以intent为对象,那intent的声明也需要SystemUIService啊,因此我们可以据此搜索关键词"SystemUIService"。

经过漫长的搜索和比对之后发现,原来,SystemUIService是在SystemServer.java中被启动的,如下所示:

[java] view plaincopy
  1. staticfinalvoidstartSystemUi(Contextcontext){
  2. Intentintent=newIntent();
  3. intent.setComponent(newComponentName("com.android.systemui",
  4. "com.android.systemui.SystemUIService"));
  5. Slog.d(TAG,"Startingservice:"+intent);
  6. context.startService(intent);
  7. }

这里的startSystemUi()方法则在ServerThread的run()方法中被调用。这里提到SystemServer就不得不提及Android的启动流程,这里不会展开详细讨论具体的流程,只是简单的介绍一下大概流程,用以表明SystemServer所处的位置。

Android的启动分为内核启动、Android启动、launcher启动,我们的SystemServer就处于Android启动中,以下是大致流程图:

init->ServiceManager->Zygote->SystemServer->... ...

在SystemServer中,初始化了Android系统中的Java层服务,如PowerManagerService、WindowManagerService等等,当然也包括了SystemUIService,它们通过ServiceManager的addService()方法,添加到ServiceManager的管理中。实际上,根据后面的分析这里add了一个很重要的StatusBarManagerService。这个Service在后面会用到的。

既然到这里SystemUIService已经启动,那么我们就继续跟踪该Service吧。

1).首先查看其onCreate()方法,如下:

[java] view plaincopy
  1. publicvoidonCreate(){
  2. //Pickstatusbarorsystembar.
  3. IWindowManagerwm=IWindowManager.Stub.asInterface(
  4. ServiceManager.getService(Context.WINDOW_SERVICE));
  5. try{
  6. SERVICES[0]=wm.canStatusBarHide()//根据wm.canStatusBarHide()判断设备类型
  7. ?R.string.config_statusBarComponent
  8. :R.string.config_systemBarComponent;
  9. }catch(RemoteExceptione){
  10. Slog.w(TAG,"Failingcheckingwhetherstatusbarcanhide",e);
  11. }
  12. finalintN=SERVICES.length;
  13. mServices=newSystemUI[N];
  14. for(inti=0;i<N;i++){
  15. Classcl=chooseClass(SERVICES[i]);
  16. Slog.d(TAG,"loading:"+cl);
  17. try{
  18. mServices[i]=(SystemUI)cl.newInstance();
  19. }catch(IllegalAccessExceptionex){
  20. thrownewRuntimeException(ex);
  21. }catch(InstantiationExceptionex){
  22. thrownewRuntimeException(ex);
  23. }
  24. mServices[i].mContext=this;
  25. Slog.d(TAG,"running:"+mServices[i]);
  26. mServices[i].start();
  27. }
  28. }

在这段代码中,通过AIDL的方式获取了WindowManager的对象wm,并调用其方法canStatusBarHide()来判断当前设备的类型,也就是说如果我们使用的Phone那么后续就会加载StatusBar和NivagationBar;而如果我们设备类型是Tablet(TV)之类的(可以在配置文档里面配置), 就会加载CombiedBar。

这里的canStatusBarHide()方法的具体实现是在:frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java。为什么会是这里呢?我们在Eclipse中导入源码之后,找到SystemUIService.java中的wm.canStatusBarHide()方法,通过open Implementation直接跳转到WindowsManagerService中:

[java] view plaincopy
  1. publicbooleancanStatusBarHide(){
  2. returnmPolicy.canStatusBarHide();
  3. }

但这里我们发现canStatusBarHide()实际上是WindowManagerPolicy的对象调用的方法,而WindowManagerPolicy只是一个接口类,根据以往分析的经验可以知道,这里的WindowManagerPolicy对象所调用的canStatusBartHide()方法一定是其实现类中的 方法。因此,继续通过open Implementation跳转,来到了PhoneWindownManager中:

[java] view plaincopy
  1. publicbooleancanStatusBarHide(){
  2. returnmStatusBarCanHide;
  3. }

继续查看mSatuBarCanHide的实现,如下所示:

[java] view plaincopy
  1. //Determinewhetherthestatusbarcanhidebasedonthesize
  2. //ofthescreen.Weassumesizes>600dparetabletswherewe
  3. //willusethesystembar.
  4. intshortSizeDp=shortSize
  5. *DisplayMetrics.DENSITY_DEFAULT
  6. /DisplayMetrics.DENSITY_DEVICE;
  7. mStatusBarCanHide=shortSizeDp<600;
  8. mStatusBarHeight=mContext.getResources().getDimensionPixelSize(
  9. mStatusBarCanHide
  10. ?com.android.internal.R.dimen.status_bar_height
  11. :com.android.internal.R.dimen.system_bar_height);
  12. mHasNavigationBar=mContext.getResources().getBoolean(
  13. com.android.internal.R.bool.config_showNavigationBar);

这里通过shortSizeDp来判断当前设备的类型,如果当前屏幕的shortSize Dp<600dp,则系统会认为该设备是Phone反之则认为是Tablet。根据mStatusBarCanHide的值,设定StatusBar或者SystemBar(CombinedBar)的高度,以及是否显示NavigationBar。

继续回到我们的SystemUIService.java的onCreate()方法中,根据前面对canStatusBarHide()的判断,SERVICE[0]中将存放R.string.config_statusBarComponent或者R.string.config_systemBarComponent。它们的值具体是:

[html] view plaincopy
  1. <stringname="config_statusBarComponent"translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>
  2. <stringname="config_systemBarComponent"translatable="false">com.android.systemui.statusbar.tablet.TabletStatusBar</string>

因为我的测试设备是Phone,那么现在SERVICE[0]中存放的就是com.android.systemui.statusbart.phone.PhoneStatusBar。查看以下代码:

[java] view plaincopy
  1. finalintN=SERVICES.length;
  2. mServices=newSystemUI[N];
  3. for(inti=0;i<N;i++){
  4. Classcl=chooseClass(SERVICES[i]);
  5. Slog.d(TAG,"loading:"+cl);
  6. try{
  7. mServices[i]=(SystemUI)cl.newInstance();
  8. }catch(IllegalAccessExceptionex){
  9. thrownewRuntimeException(ex);
  10. }catch(InstantiationExceptionex){
  11. thrownewRuntimeException(ex);
  12. }
  13. mServices[i].mContext=this;
  14. Slog.d(TAG,"running:"+mServices[i]);
  15. mServices[i].start();
  16. }

这些方法会分别启动两个方法,这两个方法可以从log中知道,分别是PhoneStatusBar.start()和PowerUI.start()。而我们的目的是要弄清SystemUI的启动,因此现关注PhoneStatusBar.start()方法。

log信息:

06-04 13:23:15.379: DEBUG/SystemUIService(396): loading: class com.android.systemui.statusbar.phone.PhoneStatusBar

06-04 13:23:16.739: DEBUG/SystemUIService(396): loading: class com.android.systemui.power.PowerUI

来到PhoneStatusBar.start()方法中,位于:SourceCode/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java,代码如下:

[java] view plaincopy
  1. @Override
  2. publicvoidstart(){
  3. mDisplay=((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
  4. .getDefaultDisplay();
  5. mWindowManager=IWindowManager.Stub.asInterface(
  6. ServiceManager.getService(Context.WINDOW_SERVICE));
  7. super.start();//callsmakeStatusBarView()
  8. addNavigationBar();
  9. //addIntruderView();
  10. //Lastly,calltotheiconpolicytoinstall/updatealltheicons.
  11. mIconPolicy=newPhoneStatusBarPolicy(mContext);
  12. }

这里的重心主要是在super.start()和addNavigationBar() 上。目前市面上很多手机已经刷入了ICS,但是大多数是没有NavigationBar的,也就是说自己修改了源码,屏蔽了NavigationBar。继续跟踪super.start()方法,来到/SourceCode/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java的start()方法中,代码如下:

[java] view plaincopy
  1. publicvoidstart(){
  2. //Firstsetupourviewsandstuff.
  3. Viewsb=makeStatusBarView();
  4. //Connectintothestatusbarmanagerservice
  5. StatusBarIconListiconList=newStatusBarIconList();
  6. ArrayList<IBinder>notificationKeys=newArrayList<IBinder>();
  7. ArrayList<StatusBarNotification>notifications=newArrayList<StatusBarNotification>();
  8. mCommandQueue=newCommandQueue(this,iconList);
  9. mBarService=IStatusBarService.Stub.asInterface(
  10. ServiceManager.getService(Context.STATUS_BAR_SERVICE));
  11. int[]switches=newint[7];
  12. ArrayList<IBinder>binders=newArrayList<IBinder>();
  13. try{
  14. mBarService.registerStatusBar(mCommandQueue,iconList,notificationKeys,notifications,
  15. switches,binders);
  16. }catch(RemoteExceptionex){
  17. //Ifthesystemprocessisn'ttherewe'redoomedanyway.
  18. }
  19. disable(switches[0]);
  20. setSystemUiVisibility(switches[1]);
  21. topAppWindowChanged(switches[2]!=0);
  22. //StatusBarManagerServicehasabackupofIMEtokenandit'srestoredhere.
  23. setImeWindowStatus(binders.get(0),switches[3],switches[4]);
  24. setHardKeyboardStatus(switches[5]!=0,switches[6]!=0);
  25. //Setuptheinitialiconstate
  26. intN=iconList.size();
  27. intviewIndex=0;
  28. for(inti=0;i<N;i++){
  29. StatusBarIconicon=iconList.getIcon(i);
  30. if(icon!=null){
  31. addIcon(iconList.getSlot(i),i,viewIndex,icon);
  32. viewIndex++;
  33. }
  34. }
  35. //Setuptheinitialnotificationstate
  36. N=notificationKeys.size();
  37. if(N==notifications.size()){
  38. for(inti=0;i<N;i++){
  39. addNotification(notificationKeys.get(i),notifications.get(i));
  40. }
  41. }else{
  42. Log.wtf(TAG,"Notificationlistlengthmismatch:keys="+N
  43. +"notifications="+notifications.size());
  44. }
  45. //Putuptheview
  46. finalintheight=getStatusBarHeight();
  47. finalWindowManager.LayoutParamslp=newWindowManager.LayoutParams(
  48. ViewGroup.LayoutParams.MATCH_PARENT,
  49. height,
  50. WindowManager.LayoutParams.TYPE_STATUS_BAR,
  51. WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
  52. |WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
  53. |WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
  54. //WeuseapixelformatofRGB565forthestatusbartosavememorybandwidthand
  55. //toensurethatthelayercanbehandledbyHWComposer.Onsomedevicesthe
  56. //HWComposerisunabletohandleSW-renderedRGBX_8888layers.
  57. PixelFormat.RGB_565);
  58. //thestatusbarshouldbeinanoverlayifpossible
  59. finalDisplaydefaultDisplay
  60. =((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
  61. .getDefaultDisplay();
  62. //WeexplicitlyleaveFLAG_HARDWARE_ACCELERATEDoutoftheflags.Thestatusbaroccupies
  63. //verylittlescreenreal-estateandisupdatedfairlyfrequently.ByusingCPUrendering
  64. //forthestatusbar,wepreventtheGPUfromhavingtowakeupjusttodothesesmall
  65. //updates,whichshouldhelpkeeppowerconsumptiondown.
  66. lp.gravity=getStatusBarGravity();
  67. lp.setTitle("StatusBar");
  68. lp.packageName=mContext.getPackageName();
  69. lp.windowAnimations=R.style.Animation_StatusBar;
  70. WindowManagerImpl.getDefault().addView(sb,lp);
  71. if(SPEW){
  72. Slog.d(TAG,"Addedstatusbarview:gravity=0x"+Integer.toHexString(lp.gravity)
  73. +"icons="+iconList.size()
  74. +"disabled=0x"+Integer.toHexString(switches[0])
  75. +"lights="+switches[1]
  76. +"menu="+switches[2]
  77. +"imeButton="+switches[3]
  78. );
  79. }
  80. mDoNotDisturb=newDoNotDisturb(mContext);
  81. }

在这里,完成了SystemUI的整个初始化以及设置过程,并最终呈现到界面上。在StatusBar中的start()方法主要完成了以下几个工作:首先获取需要在StatusBar上显示的各种icons。然后初始化一些属性。最后通过WindowManager的addView方法将StatusBar显示出来。分析到这里可能有人会问了,明明说分析的是SystemUI的嘛,怎么最后变成StatusBar了呢?如果你硬要说我跑题那我也没有办法,回过头去看看addNavigationBar(),你会发现和StatusBar的加载几乎一致,因此没必要再详述了。 如果细心阅读了的朋友肯定会发现这句代码:

mBarService = IStatusBarService.Stub.asInterface(ServiceManager.getService(Context.STATUS_BAR_SERVICE));

这不正是我们前面add的StatusBarManagerSerivce吗?这里通过AIDL的方式来获取它的对象。

整个代码执行的时序图如图2.2所示:

图 2.2

3.总结

Android 4.0的SystemUI加载启动的过程大致就是这样,虽然看似简单,但这仅仅是个开始,master还是后面呢!!各家厂商根据自家的需求,需要定制SystemUI或者美化SystemUI,不同的平台(QCOM、MTK等等)也会有不同的修改,但大体框架是没有变的,无非是在原有基础上的修修改改或者增加一些自己的类等等。通过对Android源码框架性的理解,可以学习到很多设计上的知识(虽然自己还很欠缺)。通过这次分析,开始逐渐用StarUML来画时序图,这也是一个学习的过程。

Android 4.0 ICS SystemUI浅析——SystemUI启动流程相关推荐

  1. Android 4.0 ICS SystemUI浅析——StatusBar加载流程分析

    前面两篇文章< Android 4.0 ICS SystemUI浅析--SystemUI启动流程>.< Android 4.0 ICS SystemUI浅析--StatusBar结构 ...

  2. Android 4.0 ICS SystemUI浅析——StatusBar结构分析

    在上一篇文章<Android 4.0 ICS SystemUI浅析--SystemUI启动流程>中以及提到了SystemUI的组成,本文主要分析其中的StatusBar结构. 1.布局概览 ...

  3. Android10.0系统启动之Launcher(桌面)启动流程-[Android取经之路]

    摘要:上一节我们讲完了Android10.0的ActivityManagerService的启动流程,在AMS的最后启动了Launcher进程,今天我们就来看看Launcher的真正启动流程. 阅读本 ...

  4. android 获取快捷开关_干货水帖:Android 6.0 如何从快速开关面板启动微信扫一扫?...

    自从 iOS 开放了 Widget 接口之后,很多「勇士」就探索 Widget 的各种可能性.比如前段时间很火的 Steve,让你不开锁屏就能玩到 Chrome 中内置的恐龙跳小游戏. iOS 的 W ...

  5. Android 7.1.1 锁屏界面启动流程

    前几天遇到一个低概率复现锁屏界面不显示,只显示状态栏的问题,跟了下锁屏界面启动显示的流程,在这分享下,也方便以后自己查看.前面简单介绍了下Zygote启动流程, Zygote进程启动后会首先创建一个S ...

  6. Android 8.0 学习(23)---recovery 流程分析

    Android 8.0 recovery 流程分析 这里主要分析non A/B模式下的recovery流程  A/B模式下的recovery在boot中  后续会不断补充,如果有疏漏或者错误的地方,请 ...

  7. Android进阶——Small源码分析之启动流程详解

    前言 插件化现在已经是Android工程师必备的技能之一,只是学会怎么使用是不行的,所以蹭有时间研究一下Small的源码.对于插件化主要解决的问题是四大组件的加载和资源的加载,读懂所有Small源码需 ...

  8. Android 11(platfrom 30)APP启动流程(含Activity)核心点记录

    前言:边分析,边记录 不断更新. 注意:阅读此文请同时打开android-30的源码,一步一步按文章同步跟进阅读源码,因为源码量太大,这里只能贴出部分核心代码. 场景一.从桌面点击APP图标进行启动 ...

  9. 从0移植uboot (二) _uboot启动流程分析

    经过了上一篇的配置,我们已经执行make就可以编译出一个uboot.bin,但这还不够,首先,此时的uboot并不符合三星芯片对bootloader的格式要求,同时,此时的uboot.bin也没有结合 ...

最新文章

  1. 图十字链表并求度c语言,利用十字链表存储树结构(便于同时求出某一点的入度与出度)------C语言实现...
  2. plus 什么是mybais_MyBatis和MyBatisPlus的区别是什么?
  3. 河南省第十届大学生程序设计竞赛 A,B,C,D,F,G,H 题解
  4. c程序隐藏linux,linux 下隐藏进程的一种方法
  5. hash 数据类型的应用场景
  6. 参数调优为什么要采样_3年Java外包,内推阿里过关斩将,最后却倒在调优经验上! - Java架构师追风...
  7. Nginx+Tomcat搭建集群环境
  8. 以正确的方式招募数据科学家!
  9. 【学习 OpenCV】—— 图像减色(color reduced)
  10. 【ML小结6】关联分析与序列模式关联分析
  11. 示教器重定位下机器人动作_ABB机器人示教器入门-初级教学|干货
  12. 关于系统迁移到固态硬盘后,新系统引导项丢失的问题
  13. python打印斐波拉契数列
  14. telnet无法打开到主机的连接。 在端口 23: 连接失败
  15. 维修服务器bga是什么,服务器主板芯片坏了有机器能拆除焊接BGA吗?
  16. 为Java开疆拓土的ZGC深度剖析
  17. 计算机学院运动会方阵策划案,运动会方阵策划书模板
  18. 微信开发者工具 wxmi修改模版颜色_Logo设计模版与设计管理
  19. 英伟达FXAA抗锯齿性能实测、画质对比
  20. php h2数据库,H2数据库使用

热门文章

  1. 创建 OVS vlan100 netwrok - 每天5分钟玩转 OpenStack(137)
  2. TiKV 源码解析系列 ——Placement Driver
  3. weka特征选择源码阅读
  4. Picasso detected an unsupported OkHttp on the ...
  5. javascript语言扩展:可迭代对象(3)
  6. Groovy操纵集合秘籍
  7. terminal中常用的rvm指令
  8. percona-toolkit 之 【pt-deadlock-logger】说明
  9. [置顶] “非主流”Web容器之TomJetty之让服务动起来
  10. OpenStack(1)