精华内容
参与话题
问答
  • Android 4.0 ICS SystemUI浅析——SystemUI启动流程

    万次阅读 多人点赞 2012-06-07 09:22:03
    本文只是对SystemUI分析的一个开始——启动流程的分析,网上有很多关于2.3的SystemUI的分析,可4.0与2.3的差别还是很大的,为了给自己留下笔记同时也方便大家学习和探讨,遂写此文,后续将有更多关于SystemUI的分析...
    
    

           阅读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可以看到:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
            package="com.android.systemui"
            coreApp="true"
            android:sharedUserId="android.uid.system"
            android:process="system"
            >
    
        <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
        <uses-permission android:name="android.permission.BLUETOOTH" />
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
        <uses-permission android:name="android.permission.GET_TASKS" />
        <uses-permission android:name="android.permission.MANAGE_USB" />
    
        <application
            android:persistent="true"
            android:allowClearUserData="false"
            android:allowBackup="false"
            android:hardwareAccelerated="true"
            android:label="@string/app_label"
            android:icon="@drawable/ic_launcher_settings">
    
            <!-- Broadcast receiver that gets the broadcast at boot time and starts
                 up everything else.
                 TODO: Should have an android:permission attribute
                 -->
            <service android:name="SystemUIService"
                android:exported="true"
                />
    
            <!-- started from PhoneWindowManager
                 TODO: Should have an android:permission attribute -->
            <service android:name=".screenshot.TakeScreenshotService"
                android:process=":screenshot"
                android:exported="false" />
    
            <service android:name=".LoadAverageService"
                    android:exported="true" />
    
            <service android:name=".ImageWallpaper"
                    android:permission="android.permission.BIND_WALLPAPER"
                    android:exported="true" />
    
            <receiver android:name=".BootReceiver" >
                <intent-filter>
                    <action android:name="android.intent.action.BOOT_COMPLETED" />
                </intent-filter>
            </receiver>
            ... ...
        </application>
    </manifest>


     

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

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

        static final void startSystemUi(Context context) {
            Intent intent = new Intent();
            intent.setComponent(new ComponentName("com.android.systemui",
                        "com.android.systemui.SystemUIService"));
            Slog.d(TAG, "Starting service: " + intent);
            context.startService(intent);
        }

    这里的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()方法,如下:

        public void onCreate() {
            // Pick status bar or system bar.
            IWindowManager wm = IWindowManager.Stub.asInterface(
                    ServiceManager.getService(Context.WINDOW_SERVICE));
            try {
                SERVICES[0] = wm.canStatusBarHide()//根据wm.canStatusBarHide()判断设备类型
                        ? R.string.config_statusBarComponent
                        : R.string.config_systemBarComponent;
            } catch (RemoteException e) {
                Slog.w(TAG, "Failing checking whether status bar can hide", e);
            }
    
            final int N = SERVICES.length;
            mServices = new SystemUI[N];
            for (int i=0; i<N; i++) {
                Class cl = chooseClass(SERVICES[i]);
                Slog.d(TAG, "loading: " + cl);
                try {
                    mServices[i] = (SystemUI)cl.newInstance();
                } catch (IllegalAccessException ex) {
                    throw new RuntimeException(ex);
                } catch (InstantiationException ex) {
                    throw new RuntimeException(ex);
                }
                mServices[i].mContext = this;
                Slog.d(TAG, "running: " + mServices[i]);
                mServices[i].start();
            }
        }

    在这段代码中,通过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中:

        public boolean canStatusBarHide() {
            return mPolicy.canStatusBarHide();
        }

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

        public boolean canStatusBarHide() {
            return mStatusBarCanHide;
        }

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

            // Determine whether the status bar can hide based on the size
            // of the screen.  We assume sizes > 600dp are tablets where we
            // will use the system bar.
            int shortSizeDp = shortSize
                    * DisplayMetrics.DENSITY_DEFAULT
                    / DisplayMetrics.DENSITY_DEVICE;
            mStatusBarCanHide = shortSizeDp < 600;
            mStatusBarHeight = mContext.getResources().getDimensionPixelSize(
                    mStatusBarCanHide
                    ? com.android.internal.R.dimen.status_bar_height
                    : com.android.internal.R.dimen.system_bar_height);
    
            mHasNavigationBar = mContext.getResources().getBoolean(
                    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。它们的值具体是:

    <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>
    <string name="config_systemBarComponent" translatable="false">com.android.systemui.statusbar.tablet.TabletStatusBar</string>

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

            final int N = SERVICES.length;
            mServices = new SystemUI[N];
            for (int i=0; i<N; i++) {
                Class cl = chooseClass(SERVICES[i]);
                Slog.d(TAG, "loading: " + cl);
                try {
                    mServices[i] = (SystemUI)cl.newInstance();
                } catch (IllegalAccessException ex) {
                    throw new RuntimeException(ex);
                } catch (InstantiationException ex) {
                    throw new RuntimeException(ex);
                }
                mServices[i].mContext = this;
                Slog.d(TAG, "running: " + mServices[i]);
                mServices[i].start();
            }

    这些方法会分别启动两个方法,这两个方法可以从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,代码如下:

        @Override
        public void start() {
            mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
                    .getDefaultDisplay();
    
            mWindowManager = IWindowManager.Stub.asInterface(
                    ServiceManager.getService(Context.WINDOW_SERVICE));
    
            super.start(); // calls makeStatusBarView()
    
            addNavigationBar();
    
            //addIntruderView();
    
            // Lastly, call to the icon policy to install/update all the icons.
            mIconPolicy = new PhoneStatusBarPolicy(mContext);
        }

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

        public void start() {
            // First set up our views and stuff.
            View sb = makeStatusBarView();
    
            // Connect in to the status bar manager service
            StatusBarIconList iconList = new StatusBarIconList();
            ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
            ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
            mCommandQueue = new CommandQueue(this, iconList);
            mBarService = IStatusBarService.Stub.asInterface(
                    ServiceManager.getService(Context.STATUS_BAR_SERVICE));
            int[] switches = new int[7];
            ArrayList<IBinder> binders = new ArrayList<IBinder>();
            try {
                mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
                        switches, binders);
            } catch (RemoteException ex) {
                // If the system process isn't there we're doomed anyway.
            }
    
            disable(switches[0]);
            setSystemUiVisibility(switches[1]);
            topAppWindowChanged(switches[2] != 0);
            // StatusBarManagerService has a back up of IME token and it's restored here.
            setImeWindowStatus(binders.get(0), switches[3], switches[4]);
            setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
    
            // Set up the initial icon state
            int N = iconList.size();
            int viewIndex = 0;
            for (int i=0; i<N; i++) {
                StatusBarIcon icon = iconList.getIcon(i);
                if (icon != null) {
                    addIcon(iconList.getSlot(i), i, viewIndex, icon);
                    viewIndex++;
                }
            }
    
            // Set up the initial notification state
            N = notificationKeys.size();
            if (N == notifications.size()) {
                for (int i=0; i<N; i++) {
                    addNotification(notificationKeys.get(i), notifications.get(i));
                }
            } else {
                Log.wtf(TAG, "Notification list length mismatch: keys=" + N
                        + " notifications=" + notifications.size());
            }
    
            // Put up the view
            final int height = getStatusBarHeight();
    
            final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    height,
                    WindowManager.LayoutParams.TYPE_STATUS_BAR,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
                    // We use a pixel format of RGB565 for the status bar to save memory bandwidth and
                    // to ensure that the layer can be handled by HWComposer.  On some devices the
                    // HWComposer is unable to handle SW-rendered RGBX_8888 layers.
                    PixelFormat.RGB_565);
            
            // the status bar should be in an overlay if possible
            final Display defaultDisplay 
                = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
                    .getDefaultDisplay();
    
            // We explicitly leave FLAG_HARDWARE_ACCELERATED out of the flags.  The status bar occupies
            // very little screen real-estate and is updated fairly frequently.  By using CPU rendering
            // for the status bar, we prevent the GPU from having to wake up just to do these small
            // updates, which should help keep power consumption down.
    
            lp.gravity = getStatusBarGravity();
            lp.setTitle("StatusBar");
            lp.packageName = mContext.getPackageName();
            lp.windowAnimations = R.style.Animation_StatusBar;
            WindowManagerImpl.getDefault().addView(sb, lp);
    
            if (SPEW) {
                Slog.d(TAG, "Added status bar view: gravity=0x" + Integer.toHexString(lp.gravity) 
                       + " icons=" + iconList.size()
                       + " disabled=0x" + Integer.toHexString(switches[0])
                       + " lights=" + switches[1]
                       + " menu=" + switches[2]
                       + " imeButton=" + switches[3]
                       );
            }
    
            mDoNotDisturb = new DoNotDisturb(mContext);
        }

    在这里,完成了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 SystemUI浅析之SystemUI启动流程

    千次阅读 2013-08-12 15:54:41
    前言:最近在研究4.2的status bar 和navigation bar,想做一下总结,后来碰到一篇文章分享一下,至于4.2上的变化以及4.2上的...我们知道Android 4.0以后SystemUI同时适用于Phone和Tablet(TV),因此,对于Phone来说Syst

    前言:最近在研究4.2的status bar 和navigation bar,想做一下总结,后来碰到一篇文章分享一下,至于4.2上的变化以及4.2上的总结,我会在后期补充上并且会一直修改和完善。参考http://blog.csdn.net/yihongyuelan

     

    我们知道Android 4.0以后SystemUI同时适用于Phone和Tablet(TV),因此,对于Phone来说SystemUI指的是:StatusBar(状态栏)、NavigationBar(导航栏)。而对于Tablet或者是TV来说SystemUI指的是:CombinedBar(包括了StatusBar和NavigationBar)。

    首先来看看SystemUI的代码位置,路径:SourceCode/frameworks/base/packages/SystemUI。

    那么接下来怎么分析呢?打开AndroidManifest.xml可以看到:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
            package="com.android.systemui"  
            coreApp="true"  
            android:sharedUserId="android.uid.system"  
            android:process="system"  
            >  
      
        <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />  
        <uses-permission android:name="android.permission.BLUETOOTH" />  
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />  
        <uses-permission android:name="android.permission.GET_TASKS" />  
        <uses-permission android:name="android.permission.MANAGE_USB" />  
      
        <application  
            android:persistent="true"  
            android:allowClearUserData="false"  
            android:allowBackup="false"  
            android:hardwareAccelerated="true"  
            android:label="@string/app_label"  
            android:icon="@drawable/ic_launcher_settings">  
      
            <!-- Broadcast receiver that gets the broadcast at boot time and starts  
                 up everything else.  
                 TODO: Should have an android:permission attribute  
                 -->  
            <service android:name="SystemUIService"  
                android:exported="true"  
                />  
      
            <!-- started from PhoneWindowManager  
                 TODO: Should have an android:permission attribute -->  
            <service android:name=".screenshot.TakeScreenshotService"  
                android:process=":screenshot"  
                android:exported="false" />  
      
            <service android:name=".LoadAverageService"  
                    android:exported="true" />  
      
            <service android:name=".ImageWallpaper"  
                    android:permission="android.permission.BIND_WALLPAPER"  
                    android:exported="true" />  
      
            <receiver android:name=".BootReceiver" >  
                <intent-filter>  
                    <action android:name="android.intent.action.BOOT_COMPLETED" />  
                </intent-filter>  
            </receiver>  
            ... ...  
        </application>  
    </manifest> 

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

    最终发现SystemUIService是在SystemServer.java中被启动的,如下所示:

    static final void startSystemUi(Context context) {  
        Intent intent = new Intent();  
        intent.setComponent(new ComponentName("com.android.systemui",  
                    "com.android.systemui.SystemUIService"));  
        Slog.d(TAG, "Starting service: " + intent);  
        context.startService(intent);  
    }  
    

    这里的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()方法,如下:

    public void onCreate() {  
        // Pick status bar or system bar.   
        IWindowManager wm = IWindowManager.Stub.asInterface(  
                ServiceManager.getService(Context.WINDOW_SERVICE));  
        try {  
            SERVICES[0] = wm.canStatusBarHide()//根据wm.canStatusBarHide()判断设备类型   
                    ? R.string.config_statusBarComponent  
                    : R.string.config_systemBarComponent;  
        } catch (RemoteException e) {  
            Slog.w(TAG, "Failing checking whether status bar can hide", e);  
        }  
      
        final int N = SERVICES.length;  
        mServices = new SystemUI[N];  
        for (int i=0; i<N; i++) {  
            Class cl = chooseClass(SERVICES[i]);  
            Slog.d(TAG, "loading: " + cl);  
            try {  
                mServices[i] = (SystemUI)cl.newInstance();  
            } catch (IllegalAccessException ex) {  
                throw new RuntimeException(ex);  
            } catch (InstantiationException ex) {  
                throw new RuntimeException(ex);  
            }  
            mServices[i].mContext = this;  
            Slog.d(TAG, "running: " + mServices[i]);  
            mServices[i].start();  
        }  
    }  
    

    在这段代码中,通过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中:

    public boolean canStatusBarHide() {  
        return mPolicy.canStatusBarHide();  
    }  

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

    public boolean canStatusBarHide() {  
        return mStatusBarCanHide;  
    }  
    

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

    // Determine whether the status bar can hide based on the size   
    // of the screen.  We assume sizes > 600dp are tablets where we   
    // will use the system bar.   
    int shortSizeDp = shortSize  
            * DisplayMetrics.DENSITY_DEFAULT  
            / DisplayMetrics.DENSITY_DEVICE;  
    mStatusBarCanHide = shortSizeDp < 600;  
    mStatusBarHeight = mContext.getResources().getDimensionPixelSize(  
            mStatusBarCanHide  
            ? com.android.internal.R.dimen.status_bar_height  
            : com.android.internal.R.dimen.system_bar_height);  
      
    mHasNavigationBar = mContext.getResources().getBoolean(  
            com.android.internal.R.bool.config_showNavigationBar);  

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

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

    <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>  
    <string name="config_systemBarComponent" translatable="false">com.android.systemui.statusbar.tablet.TabletStatusBar</string> 

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

    final int N = SERVICES.length;  
    mServices = new SystemUI[N];  
    for (int i=0; i<N; i++) {  
        Class cl = chooseClass(SERVICES[i]);  
        Slog.d(TAG, "loading: " + cl);  
        try {  
            mServices[i] = (SystemUI)cl.newInstance();  
        } catch (IllegalAccessException ex) {  
            throw new RuntimeException(ex);  
        } catch (InstantiationException ex) {  
            throw new RuntimeException(ex);  
        }  
        mServices[i].mContext = this;  
        Slog.d(TAG, "running: " + mServices[i]);  
        mServices[i].start();  
    } 

    这些方法会分别启动两个方法,这两个方法可以从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,代码如下:

     

    @Override  
    public void start() {  
        mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))  
                .getDefaultDisplay();  
      
        mWindowManager = IWindowManager.Stub.asInterface(  
                ServiceManager.getService(Context.WINDOW_SERVICE));  
      
        super.start(); // calls makeStatusBarView()   
      
        addNavigationBar();  
      
        //addIntruderView();   
      
        // Lastly, call to the icon policy to install/update all the icons.   
        mIconPolicy = new PhoneStatusBarPolicy(mContext);  
    }  

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

    public void start() {  
        // First set up our views and stuff.   
        View sb = makeStatusBarView();  
      
        // Connect in to the status bar manager service   
        StatusBarIconList iconList = new StatusBarIconList();  
        ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();  
        ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();  
        mCommandQueue = new CommandQueue(this, iconList);  
        mBarService = IStatusBarService.Stub.asInterface(  
                ServiceManager.getService(Context.STATUS_BAR_SERVICE));  
        int[] switches = new int[7];  
        ArrayList<IBinder> binders = new ArrayList<IBinder>();  
        try {  
            mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,  
                    switches, binders);  
        } catch (RemoteException ex) {  
            // If the system process isn't there we're doomed anyway.   
        }  
      
        disable(switches[0]);  
        setSystemUiVisibility(switches[1]);  
        topAppWindowChanged(switches[2] != 0);  
        // StatusBarManagerService has a back up of IME token and it's restored here.   
        setImeWindowStatus(binders.get(0), switches[3], switches[4]);  
        setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);  
      
        // Set up the initial icon state   
        int N = iconList.size();  
        int viewIndex = 0;  
        for (int i=0; i<N; i++) {  
            StatusBarIcon icon = iconList.getIcon(i);  
            if (icon != null) {  
                addIcon(iconList.getSlot(i), i, viewIndex, icon);  
                viewIndex++;  
            }  
        }  
      
        // Set up the initial notification state   
        N = notificationKeys.size();  
        if (N == notifications.size()) {  
            for (int i=0; i<N; i++) {  
                addNotification(notificationKeys.get(i), notifications.get(i));  
            }  
        } else {  
            Log.wtf(TAG, "Notification list length mismatch: keys=" + N  
                    + " notifications=" + notifications.size());  
        }  
      
        // Put up the view   
        final int height = getStatusBarHeight();  
      
        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(  
                ViewGroup.LayoutParams.MATCH_PARENT,  
                height,  
                WindowManager.LayoutParams.TYPE_STATUS_BAR,  
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  
                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING  
                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,  
                // We use a pixel format of RGB565 for the status bar to save memory bandwidth and   
                // to ensure that the layer can be handled by HWComposer.  On some devices the   
                // HWComposer is unable to handle SW-rendered RGBX_8888 layers.   
                PixelFormat.RGB_565);  
          
        // the status bar should be in an overlay if possible   
        final Display defaultDisplay   
            = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))  
                .getDefaultDisplay();  
      
        // We explicitly leave FLAG_HARDWARE_ACCELERATED out of the flags.  The status bar occupies   
        // very little screen real-estate and is updated fairly frequently.  By using CPU rendering   
        // for the status bar, we prevent the GPU from having to wake up just to do these small   
        // updates, which should help keep power consumption down.   
      
        lp.gravity = getStatusBarGravity();  
        lp.setTitle("StatusBar");  
        lp.packageName = mContext.getPackageName();  
        lp.windowAnimations = R.style.Animation_StatusBar;  
        WindowManagerImpl.getDefault().addView(sb, lp);  
      
        if (SPEW) {  
            Slog.d(TAG, "Added status bar view: gravity=0x" + Integer.toHexString(lp.gravity)   
                   + " icons=" + iconList.size()  
                   + " disabled=0x" + Integer.toHexString(switches[0])  
                   + " lights=" + switches[1]  
                   + " menu=" + switches[2]  
                   + " imeButton=" + switches[3]  
                   );  
        }  
      
        mDoNotDisturb = new DoNotDisturb(mContext);  
    }  
    

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

    展开全文
  • System UI 学习之一:system ui启动流程

    千次阅读 2013-07-16 11:46:15
    本文只是对SystemUI分析的一个开始——启动流程的分析,网上有很多关于2.3的SystemUI的分析,可4.0与2.3的差别还是很大的,为了给自己留下笔记同时也方便大家学习和探讨,遂写此文,后续将有更多关于SystemUI的分析...
    阅读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可以看到:

    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. <!-- Broadcast receiver that gets the broadcast at boot time and starts
    20. up everything else.
    21. TODO: Should have an android:permission attribute
    22. -->
    23. <serviceandroid:name="SystemUIService"
    24. android:exported="true"
    25. />
    26. <!-- started from PhoneWindowManager
    27. TODO: Should have an android:permission attribute -->
    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>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
            package="com.android.systemui"
            coreApp="true"
            android:sharedUserId="android.uid.system"
            android:process="system"
            >
    
        <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
        <uses-permission android:name="android.permission.BLUETOOTH" />
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
        <uses-permission android:name="android.permission.GET_TASKS" />
        <uses-permission android:name="android.permission.MANAGE_USB" />
    
        <application
            android:persistent="true"
            android:allowClearUserData="false"
            android:allowBackup="false"
            android:hardwareAccelerated="true"
            android:label="@string/app_label"
            android:icon="@drawable/ic_launcher_settings">
    
            <!-- Broadcast receiver that gets the broadcast at boot time and starts
                 up everything else.
                 TODO: Should have an android:permission attribute
                 -->
            <service android:name="SystemUIService"
                android:exported="true"
                />
    
            <!-- started from PhoneWindowManager
                 TODO: Should have an android:permission attribute -->
            <service android:name=".screenshot.TakeScreenshotService"
                android:process=":screenshot"
                android:exported="false" />
    
            <service android:name=".LoadAverageService"
                    android:exported="true" />
    
            <service android:name=".ImageWallpaper"
                    android:permission="android.permission.BIND_WALLPAPER"
                    android:exported="true" />
    
            <receiver android:name=".BootReceiver" >
                <intent-filter>
                    <action android:name="android.intent.action.BOOT_COMPLETED" />
                </intent-filter>
            </receiver>
            ... ...
        </application>
    </manifest>


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

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

    1. static finalvoid 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. }
        static final void startSystemUi(Context context) {
            Intent intent = new Intent();
            intent.setComponent(new ComponentName("com.android.systemui",
                        "com.android.systemui.SystemUIService"));
            Slog.d(TAG, "Starting service: " + intent);
            context.startService(intent);
        }

    这里的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()方法,如下:

    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. final int N = SERVICES.length;
    13. mServices = new SystemUI[N];
    14. for (int i=0; i<N; i++) {
    15. Class cl = chooseClass(SERVICES[i]);
    16. Slog.d(TAG, "loading: " + cl);
    17. try {
    18. mServices[i] = (SystemUI)cl.newInstance();
    19. } catch (IllegalAccessException ex) {
    20. throw new RuntimeException(ex);
    21. } catch (InstantiationException ex) {
    22. throw new RuntimeException(ex);
    23. }
    24. mServices[i].mContext = this;
    25. Slog.d(TAG, "running: " + mServices[i]);
    26. mServices[i].start();
    27. }
    28. }
        public void onCreate() {
            // Pick status bar or system bar.
            IWindowManager wm = IWindowManager.Stub.asInterface(
                    ServiceManager.getService(Context.WINDOW_SERVICE));
            try {
                SERVICES[0] = wm.canStatusBarHide()//根据wm.canStatusBarHide()判断设备类型
                        ? R.string.config_statusBarComponent
                        : R.string.config_systemBarComponent;
            } catch (RemoteException e) {
                Slog.w(TAG, "Failing checking whether status bar can hide", e);
            }
    
            final int N = SERVICES.length;
            mServices = new SystemUI[N];
            for (int i=0; i<N; i++) {
                Class cl = chooseClass(SERVICES[i]);
                Slog.d(TAG, "loading: " + cl);
                try {
                    mServices[i] = (SystemUI)cl.newInstance();
                } catch (IllegalAccessException ex) {
                    throw new RuntimeException(ex);
                } catch (InstantiationException ex) {
                    throw new RuntimeException(ex);
                }
                mServices[i].mContext = this;
                Slog.d(TAG, "running: " + mServices[i]);
                mServices[i].start();
            }
        }

    在这段代码中,通过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中:

    1. public boolean canStatusBarHide() {
    2. return mPolicy.canStatusBarHide();
    3. }
        public boolean canStatusBarHide() {
            return mPolicy.canStatusBarHide();
        }

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

    1. public boolean canStatusBarHide() {
    2. return mStatusBarCanHide;
    3. }
        public boolean canStatusBarHide() {
            return mStatusBarCanHide;
        }

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

    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. mHasNavigationBar = mContext.getResources().getBoolean(
    13. com.android.internal.R.bool.config_showNavigationBar);
            // Determine whether the status bar can hide based on the size
            // of the screen.  We assume sizes > 600dp are tablets where we
            // will use the system bar.
            int shortSizeDp = shortSize
                    * DisplayMetrics.DENSITY_DEFAULT
                    / DisplayMetrics.DENSITY_DEVICE;
            mStatusBarCanHide = shortSizeDp < 600;
            mStatusBarHeight = mContext.getResources().getDimensionPixelSize(
                    mStatusBarCanHide
                    ? com.android.internal.R.dimen.status_bar_height
                    : com.android.internal.R.dimen.system_bar_height);
    
            mHasNavigationBar = mContext.getResources().getBoolean(
                    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。它们的值具体是:

    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>
    <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>
    <string name="config_systemBarComponent" translatable="false">com.android.systemui.statusbar.tablet.TabletStatusBar</string>

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

    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. }
            final int N = SERVICES.length;
            mServices = new SystemUI[N];
            for (int i=0; i<N; i++) {
                Class cl = chooseClass(SERVICES[i]);
                Slog.d(TAG, "loading: " + cl);
                try {
                    mServices[i] = (SystemUI)cl.newInstance();
                } catch (IllegalAccessException ex) {
                    throw new RuntimeException(ex);
                } catch (InstantiationException ex) {
                    throw new RuntimeException(ex);
                }
                mServices[i].mContext = this;
                Slog.d(TAG, "running: " + mServices[i]);
                mServices[i].start();
            }

    这些方法会分别启动两个方法,这两个方法可以从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,代码如下:

    1. @Override
    2. public void start() {
    3. mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
    4. .getDefaultDisplay();
    5. mWindowManager = IWindowManager.Stub.asInterface(
    6. ServiceManager.getService(Context.WINDOW_SERVICE));
    7. super.start(); // calls makeStatusBarView()
    8. addNavigationBar();
    9. //addIntruderView();
    10. // Lastly, call to the icon policy to install/update all the icons.
    11. mIconPolicy = new PhoneStatusBarPolicy(mContext);
    12. }
        @Override
        public void start() {
            mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
                    .getDefaultDisplay();
    
            mWindowManager = IWindowManager.Stub.asInterface(
                    ServiceManager.getService(Context.WINDOW_SERVICE));
    
            super.start(); // calls makeStatusBarView()
    
            addNavigationBar();
    
            //addIntruderView();
    
            // Lastly, call to the icon policy to install/update all the icons.
            mIconPolicy = new PhoneStatusBarPolicy(mContext);
        }

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

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

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

    4、附上system ui 修改后可编译运行的源代码地址

    展开全文
  • 要求:修改SystemUI锁屏界面时间格式 Android P 上Sysyemui锁屏界面上的日期显示不在DateView处理了,使用KeyguardSliceProvider来处理,继承Contentprovide 之前Android O修改日期直接是在 SystemUI/src/...

    要求:修改SystemUI锁屏界面时间格式

    Android P 上Sysyemui锁屏界面上的日期显示不在DateView处理了,使用KeyguardSliceProvider来处理,继承Contentprovide

    之前Android O修改日期直接是在
    SystemUI/src/com/android/systemui/statusbar/policy/DateView.java

        public DateView(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray a = context.getTheme().obtainStyledAttributes(
                    attrs,
                    R.styleable.DateView,
                    0, 0);
    
            try {
                mDatePattern = a.getString(R.styleable.DateView_datePattern);
            } finally {
                a.recycle();
            }
            if (mDatePattern == null) {
                mDatePattern = getContext().getString(R.string.system_ui_date_pattern);
            }
    +
    +        String date_format = android.provider.Settings.System.getString(
    +                getContext().getContentResolver(),
    +                android.provider.Settings.System.DATE_FORMAT);
    +        
    +        if(!TextUtils.isEmpty(date_format)){
    +            mDatePattern = date_format;
    +        }
    +
        }
    

    Android P修改在SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java

         public boolean onCreateSliceProvider() {
             mAlarmManager = getContext().getSystemService(AlarmManager.class);
             mContentResolver = getContext().getContentResolver();
             mNextAlarmController = new NextAlarmControllerImpl(getContext());
             mNextAlarmController.addCallback(this);
             mZenModeController = new ZenModeControllerImpl(getContext(), mHandler);
             mZenModeController.addCallback(this);
             mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern);
    +
    +        String date_format = android.provider.Settings.System.getString(
    +                getContext().getContentResolver(),
    +                android.provider.Settings.System.DATE_FORMAT);
    +        
    +        if(!TextUtils.isEmpty(date_format)){
    +            mDatePattern = date_format;
    +        }
    +
             registerClockUpdate();
             updateClock();
             return true;
         }
    

    分析Anndroid P上处理方式

    接受到时间变化,或这个上面初始的时候,调用updateClock方法
        /**
         * Receiver responsible for time ticking and updating the date format.
         */
        @VisibleForTesting
        final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                final String action = intent.getAction();
                if (Intent.ACTION_TIME_TICK.equals(action)
                        || Intent.ACTION_DATE_CHANGED.equals(action)
                        || Intent.ACTION_TIME_CHANGED.equals(action)
                        || Intent.ACTION_TIMEZONE_CHANGED.equals(action)
                        || Intent.ACTION_LOCALE_CHANGED.equals(action)) {
                    if (Intent.ACTION_LOCALE_CHANGED.equals(action)
                            || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
                        // need to get a fresh date format
                        mHandler.post(KeyguardSliceProvider.this::cleanDateFormat);
                    }
                    mHandler.post(KeyguardSliceProvider.this::updateClock);
                }
            }
        };
        
    
    获取最新的时间显示,然后通知contentprovide更新显示。mLastText的值与Slice关联,现实的时候就跟新了。
        protected void updateClock() {
            final String text = getFormattedDate();
            if (!text.equals(mLastText)) {
                mLastText = text;
                mContentResolver.notifyChange(mSliceUri, null /* observer */);
            }
        }
    
        protected String getFormattedDate() {
            if (mDateFormat == null) {
                final Locale l = Locale.getDefault();
                DateFormat format;
                String country = getContext().getResources().getConfiguration().locale.getCountry();
                if (country.equals("XA")) {
                    format = DateFormat.getInstanceForSkeleton("eeeMMMd", l);
                }else{
                    format = DateFormat.getInstanceForSkeleton(mDatePattern, l);
                }
                format.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE);
                mDateFormat = format;
            }
            mCurrentTime.setTime(System.currentTimeMillis());
            return mDateFormat.format(mCurrentTime);
        }
    
        @Override
        public Slice onBindSlice(Uri sliceUri) {
            ListBuilder builder = new ListBuilder(getContext(), mSliceUri);
            builder.addRow(new RowBuilder(builder, mDateUri).setTitle(mLastText));
            addNextAlarm(builder);
            addZenMode(builder);
            addPrimaryAction(builder);
            return builder.build();
        }
    
        protected void addPrimaryAction(ListBuilder builder) {
            // Add simple action because API requires it; Keyguard handles presenting
            // its own slices so this action + icon are actually never used.
            PendingIntent pi = PendingIntent.getActivity(getContext(), 0, new Intent(), 0);
            Icon icon = Icon.createWithResource(getContext(), R.drawable.ic_access_alarms_big);
            SliceAction action = new SliceAction(pi, icon, mLastText);
    
            RowBuilder primaryActionRow = new RowBuilder(builder, Uri.parse(KEYGUARD_ACTION_URI))
                .setPrimaryAction(action);
            builder.addRow(primaryActionRow);
        }
        
    
    展开全文
  • System UI 调试方法

    千次阅读 2014-12-04 10:29:15
    工作需要想了解下SystemUi的启动流程,所以需要调试下SystemUI,这样比较高效: 1、SyStemUI是随系统启动的,所以我们先要在系统启动的时候把SystemUI的启动关掉,这个在SystemServer里面,可以...
  • SystemUI 7.0学习总结一-SystemUI的启动

    千次阅读 2017-05-24 17:03:11
    最近在学习并接触SystemUI模块的工作,Keyguard做为引入库也纳入了SystemUI的大家庭,所以整体感觉SystemUI模块还是挺大的,为了日后的查阅还是要写一下笔记。笔记记录过程中参考了许多网友的文章,在本文最后的文章...
  • 这篇文章还是在于一位前辈的交流过程中汲取到的,之前也有看网上的关于SystemUI的内容,但是都没有从架构角度去琢磨SystemUI,我一位SystemUI小白就先在这里班门弄斧了,希望各位看官们抱着批评的...
  • android 6.0 SystemUI源码分析(1)-SystemUI介绍

    万次阅读 多人点赞 2016-03-06 23:48:33
    1. SystemUI介绍 SystemUI是一个系统应用,主要功能有: 1)状态栏信息显示,比如电池,wifi信号,3G/4G等icon显示 2)通知面板,比如系统消息,第三方应用消息,都是在通知面板显示。 3)近期任务栏显示面板。比如长按...
  • 1.SystemUI启动 SystemUI是核心系统应用,需要开机启动,启动SystemUI进程,是通过启动SystemUIService来实现的。 frameworks\base\services\java\com\android\server\SystemServer.java SystemServer启动后...
  • This topic has not yet been rated Rate this topic The following functions are used with Multilingual User Interface (MUI). Function Description ...Enu
  • SystemUI 剖析

    千次阅读 2017-10-08 10:10:58
    序言17年上半年主要做车机项目SystemUI的工作,一般的来说可以选择在源码的基础上定制,也可以重新编写。具体选择哪种方案,也要根据自身项目的需求和工作量来考虑,但是不管选择哪种,都需要对SystemUI源码有一定的...
  • SystemUI作为系统应用,在开机的时候就会开始加载启动,今天来学习一下它的启动流程。 SystemServer.java (frameworks\base\services\java\com\android\server) 1.最开始会调用SystemServer里面的...
  • SystemUI架构分析

    万次阅读 多人点赞 2017-02-10 17:49:21
    SystemUI架构分析SystemUI架构分析 前言 1SystemUI介绍 1SystemUI摘要 2什么是SystemUI 2SystemUI的启动过程 3SystemUI的SERVICES 1音量控制 11音量控制简介 12音量控制SERVICE的初始化 13控制音量过程 2...
  • 前端常用的UI插件

    千次阅读 2017-10-06 20:38:31
    前端用到的UI插件
  • 前言SystemUI功能介绍SystemUI主要视图 PhoneStatusBarViewPanelHolderkeyguard_bouncer 总结 前言 Android ROM开发过程中,难免会涉及到对SystemUI的修改,之前做过一些这方面的工作,现在整理下,准备按照...
  • SystemUI模块总结

    千次阅读 2019-03-05 14:13:18
    SystemUI模块总结 1,SystemUI路径: SystemUI被放在 framework/base/packages/apps/SystemUI 在该目录的二级目录src/com/android下可看到SystemUI和Keyguard两个目录 SystemUI Keyguard 由此可见如今将锁屏界面...
  • SystemUI概述 自android2.2开始,原本存在与framework-res.apk中的状态栏和下拉通知栏界面控制被分割出一个单独的apk文件,命名为SystemUI.apk,保存在System/priv-app/SystemUI/文件夹中。在SystemUI.apk中,存在着状态...
  • SystemUI启动流程

    千次阅读 2017-11-24 18:10:55
    首先我们来看一个时序图: SystemServer前面的部分不再多说,有兴趣的朋友可以参考这篇博客《Android系统启动流程》SystemServer 名为系统服务进程,负责启动 Android 系统的关键服务。SystemServer进程其实也是由...
  • SystemUI开发技巧

    千次阅读 2018-04-24 10:35:16
    dump 命令:adb shell dumpsys activity service com.android.systemui 参考文章 http://www.koffuxu.com/android/frameworks/330.html ...
  • SystemUI源码分析

    千次阅读 2015-07-15 11:03:44
    此篇文章针对android5.1 systemUI源码进行分析下面我们进行分析, 1.1 frameworks/base/packages/SystemUI/AndroidManifest.xml 我们先来看下这个文件下的内容: android:name=”.SystemUIApplication” ...
  • Android systemUI移植

    千次阅读 2015-11-23 11:01:41
    SystemUI 在Android4.3或以前版本,SystemUI是分为Tablet和Phone两种不同代码文件的,在Android4.4已经整合了phone和tablet,统一为phone,只区分phone和tv。对于Phone来说SystemUI指的是:StatusBar(状态栏)、...
  • Keyguard 和 SystemUI

    千次阅读 2016-10-28 14:25:13
    锁屏(Keyguard)是Android是一个非常重要的模块。包含的功能有:解锁(指纹、图案、PIN等)、锁屏通知显示、快速启动相机、快速拨号、显示日期/时间等。  Keyguard的代码路径如下: \frameworks\base\packages\...
  • 安卓SystemUI定制

    千次阅读 2017-08-24 19:53:38
    OS:安卓5.1 ...在这个文件SystemUI/res/layout/status_bar.xml 添加内容如下: <com.android.systemui.statusbar.policy.KeyButtonView android:layout_gravity="left" android:orientation="vertical
  • Android4.0 SystemUI

    千次阅读 2012-06-04 10:38:45
    android4.0系统可以运行于平板电脑和手机上面,这样对于状态栏来说,也是有不同风格的,从SystemUi的代码分类我就可以看出来,google考虑了不同情况下状态栏的显示等, 在源代码里面有这么两个文件夹需要注意: ...
  • Android SystemUI分析

    千次阅读 2013-12-14 23:13:12
    SystemUI即Android系统的UI界面。手机厂商需要深度定制Android系统的UI界面就需要修改这里面的东西,比如双卡手机,就需要在状态栏增加手机状态图标。 SystemUI包括: StatusBar, 状态栏,最上面那一排 ...
  • SystemUI之功能介绍和UI布局实现

    万次阅读 多人点赞 2016-01-05 22:29:14
    Android ROM开发过程中,难免会涉及到对SystemUI的修改,之前做过一些这方面的工作,现在整理下,准备按照如下章节介绍SystemUI.借此对SystemUI做下整体性回顾。

空空如也

1 2 3 4 5 ... 20
收藏数 370,563
精华内容 148,225
关键字:

systemui