zoukankan      html  css  js  c++  java
  • Android 4.0 ICS SystemUI浅析——SystemUI启动流程

    ZZ:http://blog.csdn.net/yihongyuelan/article/details/7623578

    =====================================================


          阅读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. <manifest xmlns: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.   
    8.     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />  
    9.     <uses-permission android:name="android.permission.BLUETOOTH" />  
    10.     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />  
    11.     <uses-permission android:name="android.permission.GET_TASKS" />  
    12.     <uses-permission android:name="android.permission.MANAGE_USB" />  
    13.   
    14.     <application  
    15.         android:persistent="true"  
    16.         android:allowClearUserData="false"  
    17.         android:allowBackup="false"  
    18.         android:hardwareAccelerated="true"  
    19.         android:label="@string/app_label"  
    20.         android:icon="@drawable/ic_launcher_settings">  
    21.   
    22.         <!-- Broadcast receiver that gets the broadcast at boot time and starts  
    23.              up everything else.  
    24.              TODO: Should have an android:permission attribute  
    25.              -->  
    26.         <service android:name="SystemUIService"  
    27.             android:exported="true"  
    28.             />  
    29.   
    30.         <!-- started from PhoneWindowManager  
    31.              TODO: Should have an android:permission attribute -->  
    32.         <service android:name=".screenshot.TakeScreenshotService"  
    33.             android:process=":screenshot"  
    34.             android:exported="false" />  
    35.   
    36.         <service android:name=".LoadAverageService"  
    37.                 android:exported="true" />  
    38.   
    39.         <service android:name=".ImageWallpaper"  
    40.                 android:permission="android.permission.BIND_WALLPAPER"  
    41.                 android:exported="true" />  
    42.   
    43.         <receiver android:name=".BootReceiver" >  
    44.             <intent-filter>  
    45.                 <action android:name="android.intent.action.BOOT_COMPLETED" />  
    46.             </intent-filter>  
    47.         </receiver>  
    48.         ... ...  
    49.     </application>  
    50. </manifest>  


     

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

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

    [java] view plaincopy
    1. static final void startSystemUi(Context context) {  
    2.     Intent intent = new Intent();  
    3.     intent.setComponent(new ComponentName("com.android.systemui",  
    4.                 "com.android.systemui.SystemUIService"));  
    5.     Slog.d(TAG, "Starting service: " + 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. public void onCreate() {  
    2.     // Pick status bar or system bar.  
    3.     IWindowManager wm = 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 (RemoteException e) {  
    10.         Slog.w(TAG, "Failing checking whether status bar can hide", e);  
    11.     }  
    12.   
    13.     final int N = SERVICES.length;  
    14.     mServices = new SystemUI[N];  
    15.     for (int i=0; i<N; i++) {  
    16.         Class cl = chooseClass(SERVICES[i]);  
    17.         Slog.d(TAG, "loading: " + cl);  
    18.         try {  
    19.             mServices[i] = (SystemUI)cl.newInstance();  
    20.         } catch (IllegalAccessException ex) {  
    21.             throw new RuntimeException(ex);  
    22.         } catch (InstantiationException ex) {  
    23.             throw new RuntimeException(ex);  
    24.         }  
    25.         mServices[i].mContext = this;  
    26.         Slog.d(TAG, "running: " + mServices[i]);  
    27.         mServices[i].start();  
    28.     }  
    29. }  

    在这段代码中,通过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. public boolean canStatusBarHide() {  
    2.     return mPolicy.canStatusBarHide();  
    3. }  

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

    [java] view plaincopy
    1. public boolean canStatusBarHide() {  
    2.     return mStatusBarCanHide;  
    3. }  

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

    [java] view plaincopy
    1. // Determine whether the status bar can hide based on the size  
    2. // of the screen.  We assume sizes > 600dp are tablets where we  
    3. // will use the system bar.  
    4. int shortSizeDp = 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.   
    13. mHasNavigationBar = mContext.getResources().getBoolean(  
    14.         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. <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>  
    2. <string name="config_systemBarComponent" translatable="false">com.android.systemui.statusbar.tablet.TabletStatusBar</string>  

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

    [java] view plaincopy
    1. final int N = SERVICES.length;  
    2. mServices = new SystemUI[N];  
    3. for (int i=0; i<N; i++) {  
    4.     Class cl = chooseClass(SERVICES[i]);  
    5.     Slog.d(TAG, "loading: " + cl);  
    6.     try {  
    7.         mServices[i] = (SystemUI)cl.newInstance();  
    8.     } catch (IllegalAccessException ex) {  
    9.         throw new RuntimeException(ex);  
    10.     } catch (InstantiationException ex) {  
    11.         throw new RuntimeException(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. public void start() {  
    3.     mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))  
    4.             .getDefaultDisplay();  
    5.   
    6.     mWindowManager = IWindowManager.Stub.asInterface(  
    7.             ServiceManager.getService(Context.WINDOW_SERVICE));  
    8.   
    9.     super.start(); // calls makeStatusBarView()  
    10.   
    11.     addNavigationBar();  
    12.   
    13.     //addIntruderView();  
    14.   
    15.     // Lastly, call to the icon policy to install/update all the icons.  
    16.     mIconPolicy = new PhoneStatusBarPolicy(mContext);  
    17. }  

    这里的重心主要是在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. public void start() {  
    2.     // First set up our views and stuff.  
    3.     View sb = makeStatusBarView();  
    4.   
    5.     // Connect in to the status bar manager service  
    6.     StatusBarIconList iconList = new StatusBarIconList();  
    7.     ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();  
    8.     ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();  
    9.     mCommandQueue = new CommandQueue(this, iconList);  
    10.     mBarService = IStatusBarService.Stub.asInterface(  
    11.             ServiceManager.getService(Context.STATUS_BAR_SERVICE));  
    12.     int[] switches = new int[7];  
    13.     ArrayList<IBinder> binders = new ArrayList<IBinder>();  
    14.     try {  
    15.         mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,  
    16.                 switches, binders);  
    17.     } catch (RemoteException ex) {  
    18.         // If the system process isn't there we're doomed anyway.  
    19.     }  
    20.   
    21.     disable(switches[0]);  
    22.     setSystemUiVisibility(switches[1]);  
    23.     topAppWindowChanged(switches[2] != 0);  
    24.     // StatusBarManagerService has a back up of IME token and it's restored here.  
    25.     setImeWindowStatus(binders.get(0), switches[3], switches[4]);  
    26.     setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);  
    27.   
    28.     // Set up the initial icon state  
    29.     int N = iconList.size();  
    30.     int viewIndex = 0;  
    31.     for (int i=0; i<N; i++) {  
    32.         StatusBarIcon icon = iconList.getIcon(i);  
    33.         if (icon != null) {  
    34.             addIcon(iconList.getSlot(i), i, viewIndex, icon);  
    35.             viewIndex++;  
    36.         }  
    37.     }  
    38.   
    39.     // Set up the initial notification state  
    40.     N = notificationKeys.size();  
    41.     if (N == notifications.size()) {  
    42.         for (int i=0; i<N; i++) {  
    43.             addNotification(notificationKeys.get(i), notifications.get(i));  
    44.         }  
    45.     } else {  
    46.         Log.wtf(TAG, "Notification list length mismatch: keys=" + N  
    47.                 + " notifications=" + notifications.size());  
    48.     }  
    49.   
    50.     // Put up the view  
    51.     final int height = getStatusBarHeight();  
    52.   
    53.     final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(  
    54.             ViewGroup.LayoutParams.MATCH_PARENT,  
    55.             height,  
    56.             WindowManager.LayoutParams.TYPE_STATUS_BAR,  
    57.             WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  
    58.                 | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING  
    59.                 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,  
    60.             // We use a pixel format of RGB565 for the status bar to save memory bandwidth and  
    61.             // to ensure that the layer can be handled by HWComposer.  On some devices the  
    62.             // HWComposer is unable to handle SW-rendered RGBX_8888 layers.  
    63.             PixelFormat.RGB_565);  
    64.       
    65.     // the status bar should be in an overlay if possible  
    66.     final Display defaultDisplay   
    67.         = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))  
    68.             .getDefaultDisplay();  
    69.   
    70.     // We explicitly leave FLAG_HARDWARE_ACCELERATED out of the flags.  The status bar occupies  
    71.     // very little screen real-estate and is updated fairly frequently.  By using CPU rendering  
    72.     // for the status bar, we prevent the GPU from having to wake up just to do these small  
    73.     // updates, which should help keep power consumption down.  
    74.   
    75.     lp.gravity = getStatusBarGravity();  
    76.     lp.setTitle("StatusBar");  
    77.     lp.packageName = mContext.getPackageName();  
    78.     lp.windowAnimations = R.style.Animation_StatusBar;  
    79.     WindowManagerImpl.getDefault().addView(sb, lp);  
    80.   
    81.     if (SPEW) {  
    82.         Slog.d(TAG, "Added status bar view: gravity=0x" + Integer.toHexString(lp.gravity)   
    83.                + " icons=" + iconList.size()  
    84.                + " disabled=0x" + Integer.toHexString(switches[0])  
    85.                + " lights=" + switches[1]  
    86.                + " menu=" + switches[2]  
    87.                + " imeButton=" + switches[3]  
    88.                );  
    89.     }  
    90.   
    91.     mDoNotDisturb = new DoNotDisturb(mContext);  
    92. }  

    在这里,完成了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来画时序图,这也是一个学习的过程。


    Meet so Meet. C plusplus I-PLUS....
  • 相关阅读:
    Dede 自定义频道解析
    没有什么想说的,但既然来了,也就留下一句
    Title和META标签参数详解,SEO优化中的title和META标签的重要性
    Jsoup HTML 解析器 用法介绍
    php 相关方面内容、
    Html.ActionLink Url.Action的用法
    Asp.Net MVC2.0 Url 路由入门实例篇
    asp.net mvc 在View中获取Url参数的值
    SSH2实现数据库和界面的分页
    struts2中的ModelDriven使用
  • 原文地址:https://www.cnblogs.com/iplus/p/4467355.html
Copyright © 2011-2022 走看看