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....
  • 相关阅读:
    linux下启动和关闭网卡命令及DHCP上网
    python 编码问题
    paddlepaddle
    Convolutional Neural Network Architectures for Matching Natural Language Sentences
    deep learning RNN
    Learning Structured Representation for Text Classification via Reinforcement Learning 学习笔记
    Python IO密集型任务、计算密集型任务,以及多线程、多进程
    EM 算法最好的解释
    tensorflow 调参过程
    tensorflow 学习纪录(持续更新)
  • 原文地址:https://www.cnblogs.com/iplus/p/4467355.html
Copyright © 2011-2022 走看看