Android4.1Systemui分析

1.为什么需要Systemui?

        Systemui就是状态栏,状态栏可以说是操作系统上不可或缺的一部分,例如:Windows系统底部的状态栏,Ubuntu系统顶部的状态栏。同样,Android作为一个操作系统,状态栏也是其系统的一个重要组成部分。既然各个系统上都配备了状态栏,那么它究竟有何作用呢?其实,状态栏的作用可以分为三大块:

       1)导航功能。针对桌面系统来说,无论用户当前处于任何界面,都可以通过状态栏导航到其它应用;而对与Android系统,只能通过状态栏跳转到Settings及Notification所指定的Activity。

       2)状态的显示。例如:蓝牙的连接状态,音量的状态等。

       3)通知的显示。例如:短信,邮件等通知信息的显示。另外,还有很多厂商在状态栏中定制了快速开关,如蓝牙,wifi的打开和关闭。

2.Systemui的创建

  

        由于Systemui需要显示在应用的上层,所以需要通过WindowManager.addView()将Systemui所对应的view添加到界面;其次,Systemui需要常驻内存,所以需要通过Service来控制。

       从上面的时序图可以看到,Systemui的创建是由SystemServer发起的。SystemServer中显示的启动了SystemUIService。接下来我们来看一下SystemUIService中都有什么。

finalObject[] SERVICES = newObject[] {

    0, //system bar or status bar, filled in below.

    com.android.systemui.power.PowerUI.class,

    com.android.systemui.media.RingtonePlayer.class,

};

 

       1)首先,其中定义了一个Object数组,用来存放接下来要启动的对象。其中 PowerUI和 RingtonePlayer都继承了SystemUI这个抽象类,并实现了其中的抽象方法start()。其实PowerUI就是当设备电量不足时弹出的界面,而RingtonePlayer则是通过音量键条件音量时弹出的音量条件进度条。它们两个的实现都比较简单,就是在收到对应广播的时候显示View。

       2)其中还有一个SERVICES[0],这个才是重点,在SystemUIService的onCreate()方法中有如下代码。

SERVICES[0] =wm.hasSystemNavBar()

    ? R.string.config_systemBarComponent

    : R.string.config_statusBarComponent

       它会根据设备是否具有导航栏来判断当前设备是平板还是手机,并将对应的SystemUI子类赋值给SERVICES[0]。

<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>

       这里我们使用PhoneStatusBar,接下来执行的就是PhoneStatusBar的start()方法。

@Override

public voidstart() {

    //TODO: getthe boolean value from FW API not from system properties

    mCurrentFontScale =getSystemFontScale();

    mDisplay =((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))

                .getDefaultDisplay();

    mWindowManager =IWindowManager.Stub.asInterface(

    ServiceManager.getService(Context.WINDOW_SERVICE));

    super.start();// calls createAndAddWindows()

    addNavigationBar();

    if(ENABLE_INTRUDERS) addIntruderView();

    // Lastly, call to theicon policy to install/update all the icons.

    mIconPolicy = newPhoneStatusBarPolicy(mContext);

}

       看到上面的方法,是不是惊呆了?就这几行代码,SystemUI就被创建出来了?怎么连布局文件都没看到呢?不要慌,接下来我们才要进入主题。

super.start();// calls createAndAddWindows()

看到这行注释了吧,创建并添加Windows,是调用了父类的start()方法,不用说,找父类去。

       接下来我们转到BaseStatusBar的start()方法中,代码老多了,不过一半的代码都是变量的初始化操作,真正需要我们关心的代码其实也就两行。

mBarService.registerStatusBar(mCommandQueue,iconList, notificationKeys, notifications,switches, binders);

(这里我们只讲Systemui的创建,上面那段是与状态图标显示相关的,我们一会儿再讲)

createAndAddWindows();

       与Systemui创建相关的就是createAndAddWindows();但BaseStatusBar中并没有该方法,而是调用了其子类PhoneStatusBar的createAndAddWindows();细心的同学就有疑问了,为什么我们不在这些操作都放到PhoneStatusBar中,而非要这样调来调去呢?其实原因很简单,因为这部分的代码会被TableStatusBar复用,所以才这样设计。

       废话不多说,我们重新回到PhoneStatusBar中,通过createAndAddWindows()执行到了addStatusBarWindow(),里面用调用了makeStatusBarView(),状态栏的布局文件就是在该方法中被加载进来的。

mStatusBarWindow =(StatusBarWindowView) View.inflate(context,

                    R.layout.super_status_bar, null);

       从上面的代码可以看到,Systemui中的总布局文件就是 super_status_bar.xml。当布局文件被加载后,就在addStatusBarWindow()中通过WindowManager将systemui显示到界面上。

WindowManagerImpl.getDefault().addView(mStatusBarWindow, lp);

至此,Systemui的创建就完成了。

3.状态栏图标的添加

              当插入耳机的时候,在状态栏上会出现一个耳机图标;当bt打开时,在状态栏也会出现对应的图标,我们暂且称这些图标为状态栏图标。而usb连接时的提示,短信来的提示等,虽然也在状态栏添加了图标,但是它与前面的耳机图标是有区别的,我们暂且称呼这些图标为Notification图标。接下来,我们分析一下状态栏图标的添加流程(更新,删除流程类似)。

        我们先来想一想,Systemui怎么知道bt是否打开,耳机是否插入了?没错,就是通过广播,所以,Systemui中的状态栏图标的更新就是通过广播机制实现的,这个接收器就注册在PhoneStatusBarPolicy中,这个类的逻辑比较简单,这里不再赘述,下面我们就以bt图标的添加为例来分析一下状态栏图标的更新流程。

        当phoneStatusBarPolicy被new出来的时候,会将bt的图标添加到状态栏,并将其置为不可见,当收到bt打开或关闭的广播后会去更新bt图标的状态

mService.setIcon("bluetooth",bluetoothIcon, 0, null);

mService.setIconVisibility("bluetooth", mBluetoothEnabled);

可以看到,图标是通过mService的setIcon添加进去的,而mService是StatusBarManager的引用,通过StatusBarManager的setIcon最终会转到StatusBarManagerService的setIcon。

public voidsetIcon(String slot, String iconPackage, inticonId, int iconLevel,StringcontentDescription) {

    enforceStatusBar();

    synchronized (mIcons) {

        int index= mIcons.getSlotIndex(slot);

        if (index< 0) {

            throw newSecurityException("invalid status bar icon slot: " +slot);

        }

        StatusBarIcon icon = newStatusBarIcon(iconPackage, iconId, iconLevel, 0,contentDescription);

        mIcons.setIcon(index,icon);

        if (mBar != null) {

            try {

              mBar.setIcon(index,icon);

            } catch(RemoteException ex) {}

        }

    }

}

         在这个方法中需要注意mIcons这个变量,这个变量在StatusBarManagerService实例化的时候进行了初始化操作。

mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons));

其中config_statusBarIcons是config.xml中的一个String数组,所有想要添加到状态栏的icon都必须在该数组中添加相应条目,数组内容如下(由于里面所包含item比较多,没有全部贴上)。

<item><xliff:g id="id">ime</xliff:g></item>

<item><xliff:gid="id">sync_failing</xliff:g></item>

<item><xliff:gid="id">sync_active</xliff:g></item>

<item><xliff:gid="id">gps</xliff:g></item>

<item><xliff:gid="id">bluetooth</xliff:g></item>

        接下来我们来分析同步代码块儿中的内容,mIcons.getSlotIndex(slot)会查找mIcons中是否包含要添加的icon(即config.xml中是否添加了对应的item),如果没有,则抛出异常。这里bluetooth在config.xml中已经配置了,所以可以执行下去,mIcons.setIcon(index,icon)会把该icon对应的StatusBarIcon对象添加到StatusBarIconList中的一个数组里,StatusBarIconList中维护着两个数组:

privateString[] mSlots; //代表config_statusBarIcons字串数组

privateStatusBarIcon[] mIcons; //代表字串数组中每个条目所对应的icon信息

最后,调用mBar.setIcon(index, icon)去将图标添加到状态栏。不用说了,接下来大家肯定要问mBar是什么,我们看它的定义:

volatile IStatusBarmBar

volatile关键字是与线程同步相关的,这里不做解释,通过声明知道,mBar是 IStatusBar类型的。但mBar是在哪里初始化呢?搜索后发现在registerStatusBar方法中有mBar = bar。这就回到上节我们遗留下的那段代码,在BaseStatusBar中的start()方法里有这样一段代码:

mCommandQueue = newCommandQueue(this,iconList);

mBarService.registerStatusBar(mCommandQueue,iconList, notificationKeys, notifications,switches, binders);

StatusBarManagerService中的mBar其实就是BaseStatusBar中的mCommandQueue,而mBar.setIcon(index, icon)也就是调用CommandQueue的setIcon(),接下来就是通过Handler进行的一系列跳转了

case OP_SET_ICON: {

    StatusBarIcon icon = (StatusBarIcon)msg.obj;

    StatusBarIcon old = mList.getIcon(index);

    if (old== null) {

        mList.setIcon(index,icon);

        mCallbacks.addIcon(mList.getSlot(index),index, viewIndex, icon);

    } else {

        mList.setIcon(index,icon);

        mCallbacks.updateIcon(mList.getSlot(index),index, viewIndex,old, icon);

    }

    break;

}

从上面可以看到,如果icon不存在则添加,如果存在则更新。

这里的mList和mCallbacks都是通过CommandQueue的构造方法传递过来的,mCallbacks就是BaseStatusBar。

publicCommandQueue(Callbacks callbacks, StatusBarIconList list) {

    mCallbacks =callbacks;

    mList =list;

}

因此icon的添加和更新都会转到BaseStatusBar中执行,但BaseStatusBar中并没有对应的方法,所以就交给了其父类PhoneStatusBar执行。在PhoneStatusBar中的addIcon()执行mStatusIcons.addView(view, viewIndex, newLinearLayout.LayoutParams(mIconSize, mIconSize))就会将icon添加到状态栏。

4.Notification的显示


        当用户收到短信的时候,会在状态栏出现一条提示信息,然后会显示一个未读短信的图标,这个提示信息就是Notification。

        Notification的显示比状态栏图标的显示要简单点儿。任何应用都可以发送Notification,状态栏会将其所发送的Notification显示出来,Notification的添加和删除是由NotificationManagerService来管理的,状态栏只是负责显示而已,我们这里只讲状态栏。

        从上面的流程图可以看到,NotificationManager通过notifiy()来发送Notification,在NotificationManagerService中会根据不同的条件执行Notification的添加,更新和删除操作,这里只说添加,更新和删除的流程类似。

mStatusBar.addNotification(n);

这里的mStatusBar不用说还是CommandQueue,也就是BaseStatusBar,在BaseStatusBar中调用了父类的addNotification(),最终又调用了BaseStatusBar的addNotificationViews(),在该方法内部执行了inflateViews将Notification显示了出来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值