Android系统应用---SystemUI之二:Statusbar显示流程以及系统状态图标更新分析

StatusBar概述

状态栏主要用来显示一些系统图标,应用的通知图标和系统时间。Statusbar模块就是控制和管理着这些图标,以及通知信息的显示和一些系统开关的。状态栏在Android手机中位于屏幕的最上方,通常在各种应用的最上方都会显示。

当SystemUI和Status Bar创建完成,就通过makeStatusBarView()进行布局的加载和界面的显示。

状态栏的视图效果如下图:

Android系统应用---SystemUI之二:Statusbar显示流程以及系统状态图标更新分析_第1张图片

如图所示,状态栏分为通知图标区域,状态图标区域,信号电量组合区域和时钟区域

●通知区域显示系统发出notification的ticker,有图标和文字的简短显示

●状态图标区域显示系统设备的当前状态,例如打开了WIFI,蓝牙,GPS后,此区域都会显示相对应的图标。上图是打开的静音和闹钟图标。

●信号组合区域显示信号变化状态,当信号发生变化后此处图标都会进行相应更新。

●电量显示区域显示电量的变化状态

●时钟区域按指定格式显示系统的当前时间。

StatusBar 实现原理:

 

               Android系统应用---SystemUI之二:Statusbar显示流程以及系统状态图标更新分析_第2张图片

 

从类图中我们可以看到几个重要的类和方法

BaseStatusBar:状态栏核心类,是一个抽象类,它的start方法定义了状态栏启动时的具体步骤,负责绑定远程代理接口以及管理窗口。

PhoneStatusBar:手机状态栏的具体实现类

PhoneStatusBarPolicy:定义了系统通知图标的设置策略,监听图标改变广播

CommandQueue:继承自IStatusBar.stub远程接口,继承自IStatusBar.Stub,是IStatusBar的服务端,是IStatusBarService与BaseStatusBar进行通信的桥梁。

 

实现的关键步骤:

1.      获取StatusBarManagerService 的实例,获取一端连接

StatusBarManagerService是运行于SystemServer的一个系统服务。并由ServiceManager管理,它比StatusBar创建的早,继承IStatusBarService.Stub,它接受用户操作状态栏的请求并将其转发给BaseStatusBar。为了保证SystemUI 意外退出后不会发生信息丢失,保存了所有需要状态栏与导航栏进行显示或处理的信息副本。StatusBarManagerService 为那些对状态栏感兴趣的其他系统服务定义了一系列接口,对SystemUI而言,它是一个客户端,因为framework收到请求后,StatusBarManagerService 会将用户操作状态栏的请求发送给SystemUI,并由后者完成响应 。

 

        mBarService = IStatusBarService.Stub.asInterface(

               ServiceManager.getService(Context.STATUS_BAR_SERVICE));

 

2.将一个继承自IStatusBar.Stub的CommandQueue的实例注册到IStatusBarService以建立通信,并将信息副本取回,实现双向连接。registerStatusBar()保存BaseStatusBar中的CommandQueue的Bp端到mBar成员之中,然后再把信息副本填充到参数里去。StatusBarManagerService将通过mBar与BaseStatusBar进行通信。

        mCommandQueue = new CommandQueue(this, iconList);

        try {

            mBarService.registerStatusBar(mCommandQueue,iconList, notificationKeys, notifications,

                    switches, binders);

        } catch (RemoteException ex) {

            // If the system process isn't there we're doomedanyway.

        }

 

  3.通过调用子类PhoneStatusBar的createAndAddWindows()方法完成状态栏与导航栏的控件树及窗口的创建与显示。

 


系统图标状态更新



StatusBar中,通知图标不受限制,发送通知即可显示,信号和电池以及时间图标相对独立,本文主要介绍系统状态区图标

系统状态区图标一般用于提示用户当前的系统状态,蓝牙、闹钟、同步、飞行模式、情景模式等图标都显示在这一区域,由于表示的是系统的状态,所以状态栏对这些图标的意图进行了严格的限定。图标的意图由一个字符串表示,StatusBarManagerService维护了一个准许显示的预定义的意图列表。

在 StatusBarManagerService 构造的时候会去创建 mIcons,它是一个 StatusBarIconList 对象,然后调用了 defineSlots()方法,初始化数据,根据string加载Icon

 

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

这个列表定义在/frameworks/base/core/res/res/values/config.xml

 

BaseStatusBar 调用了 StatusBarManagerService 的 registerStatusBar 方法,并把 mIcons 拷贝给了StatusBarManagerService,这时候会进行检查,如果slot意图没有预定义,就会报出安全异常。SystemUI通过广播机制获取到图标状态的变化,进行更新,广播接收器注册在PhoneStatusBarPolicy,监听 Status icon 的状态,管理所有的图标安装策略,接收广播,创建的时候会将图标创建出来,置为不可见

 

 // listen for broadcasts

       IntentFilter filter = newIntentFilter();

       filter.addAction(Intent.ACTION_ALARM_CHANGED);

        filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);

       filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);

       filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);

       filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);

        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);

       filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);

        mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);

 

--》监听以上状态的改变,当收到状态变化后就会调

用 StatusBarManagerService 的 setIcon 函数,

public void setIcon(Stringslot, String iconPackage, inticonId, inticonLevel,

            StringcontentDescription) {

  /* 首先一样是权限检查,与registerStatusBar()不同,这次要求的是一个系统级别的权限android.permission.STATUS_BAR。因为设置系统状态区图标的操作不允许普通应用程序进行。其他的操作诸如添加一条通知则不需要此权限 */

 

       enforceStatusBar();

 

        synchronized (mIcons) {

            int index = mIcons.getSlotIndex(slot);

            if (index < 0) {

                throw newSecurityException("invalidstatus bar icon slot: " +slot);

            }

 

           StatusBarIcon icon = newStatusBarIcon(iconPackage, UserHandle.OWNER, iconId,

                   iconLevel, 0,

                   contentDescription);

            //Slog.d(TAG, "setIcon slot=" + slot +" index=" + index + " icon=" + icon);

// ① 将图标信息保存在副本之中

            mIcons.setIcon(index,icon);

     // ② 将设置请求发送给BaseStatusBar

            if (mBar != null) {

                try {

                   mBar.setIcon(index, icon);

                } catch (RemoteException ex) {

                }

            }

        }

    }

 

--》创建StatusBarIcon 到 mIcons 中,再调用

CommandQueue 的 setIcon,在 CommandQueue 中又保存了一份 icon 的 List,如果已经

添加过的,就会调用 PhoneStatusBar 的 updateIcon 方法,如果没有添加过就会调用 addIcon 方法

switch (msg.arg1) {

                       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;

                        }

从上述流程得知StatusBarManagerService的作用与工作原理如下:

●它是SystemUI中的状态栏与导航栏在system_server中的代理。所有对状态栏或导航来有需求的对象都可以通过获取StatusBarManagerService的实例或Bp端达到其目的。只不过使用者必须拥有能够完成操作的相应权限。

● 它保存了状态栏/导航栏所需的信息副本,用于在SystemUI意外退出之后的恢复。

 

 

相关流程图如下

 Android系统应用---SystemUI之二:Statusbar显示流程以及系统状态图标更新分析_第3张图片

系统图标定制开发

有些定制需求中,SystemUI的某些图标并没有显示,需要我们做定制开发,开发流程如下

1.        修改config文件,添加预定义的意图

    

     <item><xliff:g id="id">headsetxliff:g>item>

    

2.        系统所有的图标控制显示在SystemUI的StatusBarPolicy.Java文件里

在这里我们就可以添加服务和广播,监听广播事件或者监听数据库的变化

private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {

        @Override

        public void onReceive(Context context, Intent intent){

            String action =intent.getAction();

            if(action.equals(Intent.ACTION_BATTERY_CHANGED)) {

               updateBattery(intent);

            }

                //耳机广播

            }else if(action.equals(Intent.ACTION_HEADSET_PLUG)){

             updateHeadset(intent);

              //耳机广播

            }

}

3.       然后在初始化StatusBarPolicy里加入服务 publicStatusBarPolicy(Context context) ----这部分setIcon就是将slot和资源图标绑定起来

//headset

mService.setIcon("headset", R.drawable.stat_sys_headset, 0);mService.setIconVisibility("headset", false);

 

4.        定义耳机显示控制方法

//耳机图标显示控制

    private final void updateHeadset(Intent intent) {

        final String action = intent.getAction();

        if (action.equals(Intent.ACTION_HEADSET_PLUG)){

           int state = intent.getIntExtra("state", 0);

           if (state == 1) {

               mService.setIconVisibility("headset", true);

           } else {

           mService.setIconVisibility("headset", false);

           }

        }

   }

5.        添加对应的图标到资源文件夹

 

你可能感兴趣的:(Android系统应用---SystemUI之二:Statusbar显示流程以及系统状态图标更新分析)