StatusBar概述
状态栏主要用来显示一些系统图标,应用的通知图标和系统时间。Statusbar模块就是控制和管理着这些图标,以及通知信息的显示和一些系统开关的。状态栏在Android手机中位于屏幕的最上方,通常在各种应用的最上方都会显示。
当SystemUI和Status Bar创建完成,就通过makeStatusBarView()进行布局的加载和界面的显示。
状态栏的视图效果如下图:
如图所示,状态栏分为通知图标区域,状态图标区域,信号电量组合区域和时钟区域
●通知区域显示系统发出notification的ticker,有图标和文字的简短显示
●状态图标区域显示系统设备的当前状态,例如打开了WIFI,蓝牙,GPS后,此区域都会显示相对应的图标。上图是打开的静音和闹钟图标。
●信号组合区域显示信号变化状态,当信号发生变化后此处图标都会进行相应更新。
●电量显示区域显示电量的变化状态
●时钟区域按指定格式显示系统的当前时间。
StatusBar 实现原理:
从类图中我们可以看到几个重要的类和方法
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()方法完成状态栏与导航栏的控件树及窗口的创建与显示。
系统状态区图标一般用于提示用户当前的系统状态,蓝牙、闹钟、同步、飞行模式、情景模式等图标都显示在这一区域,由于表示的是系统的状态,所以状态栏对这些图标的意图进行了严格的限定。图标的意图由一个字符串表示,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意外退出之后的恢复。
相关流程图如下
系统图标定制开发
有些定制需求中,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. 添加对应的图标到资源文件夹