Android FrameWork——StatusBar
Android系统顶上的状态栏是属于FrameWork的东东,由于项目上需要对状态栏进行一些修改调整,我对其作了一个初步研究,写出来大家共享一 下,其实这些早已写了,只是想等研究StatusBar中ExtendsView后再整理一个blog,不过现在已经没有时间了,目前深入研究 Android Binder机制,废话不多少,开始进入statusbar的探索
1.先从StatusBar的布局文件入手,文件位置位置:frameworks/base/core/res/res/layout/status_bar.xml
2.我对status_bar.xml布局文件进行分析,画出结构图,以便对StatusBar有一个整体的了解:
3.com.android.server.status.StatusBarView--statusbar的最顶层view,直观上我们是看不到它的
4.LinearLayout android:id="@+id/icons" 我们看到的状态栏,系统默认是左边放通知图标notificationIcons,右边放状态图标statusIcons
--1.通知图标区域: IconMerger android:id="@+id/notificationIcons"
--2.状态图标区域:LinearLayout android:id="@+id/statusIcons"
我对status_bar.xml做了修改,notificationIcons的background="#ff0000",statusIcons的background="#0000ff",下面就是现实效果
(图1)
5.那么LinearLayout android:id="@+id/ticker"显示在哪里呢,在正常情况下ticker是不显示的,只有在StatusBarService收到通知 时它才显示,比如SD卡拔出时,我也截了一张图,在前面我已经修改了status_bar.xml并修改它 android:background="#0000ff"大家可以看一下效果:
(图2)
6.最后一个是DateView,它是在点击statusbar时才显示的,默认是隐藏的
7.StatusBar的ui框架就这些,是不是很简单,接下来,我们肯定还有些问题:StatusBarView是如何被创建的呢?上面那个状态时钟图标如何被加载的呢?通知图标如何被加载的?下面我将继续讲解。
8.StatusBarView创建
StatusBarView是如何被创建的呢,这得从system_process说起,在system_process构建时,会运行ServerThread.run方法加载各种系统服务,其中有一个服务就是StatusBarService:
看上面调用堆栈图,StatusBarService会调用一个makeStatusBarView的方法,在里面它创建了StatusBarView
9.状态图标的加载
我们前面讲过,StatusBarView的子View LinearLayout android:id="@+id/icons"它包含一个通知图标栏,见(图1)中的红色部分,一个状态图标栏:见(图1)中的蓝色部分,但是这个并没 有包含上面的图标,如时间图标(注意右边显示的时间也是一个Text类型的图标,刚开始我也不理解,在status_bar.xml中找了半天没有找到 它),那么这些图标又是怎么样被加载上去的呢?还是看ServerThread.run,在ServerThread.run中调用了
com.android.server.status.StatusBarPolicy.installIcons(context, statusBar);//装载状态栏的图标
进入这个函数看它如何实现的,installIcons静态方法直接调用了构造函数StatusBarPolicy去实现这个操作了:
private StatusBarPolicy(Context context, StatusBarService service) {
// 构建设置图标...
// 注册广播接收器,接收各种图标状态变化消息,以此更新状态栏图标
}
代码太多,我就以时间图标为例,进行代码说明,代码在StatusBarPolicy构造函数中:
//构建时间图标的IconData对象,类型为TEXT
IconData mClockData = IconData.makeText("clock", "");
--IconData.makeText(String slot, CharSequence text) {
IconData data = new IconData();
data.type = TEXT;
data.slot = slot;
data.text = text;
return data;
}
//调用addIcon方法添加一个状态图标到状态栏
IBinder mClockIcon = service.addIcon(mClockData, null);
-->IBinder service.addIcon(IconData data, NotificationData n) {
int slot;
// assert early-on if they using a slot that doesn't exist.
if (data != null && n == null) {
slot = getRightIconIndex(data.slot);
if (slot < 0) {
throw new SecurityException("invalid status bar icon slot: "
+ (data.slot != null ? "'" + data.slot + "'" : "null"));
}
} else {
slot = -1;
}
IBinder key = new Binder();
addPendingOp(OP_ADD_ICON, key, data, n, -1);
return key;
}
//获取系统时间,并更新时间图标
updateClock();
-->updateClock() {
mCalendar.setTimeInMillis(System.currentTimeMillis());
mClockData.text = getSmallTime();
mService.updateIcon(mClockIcon, mClockData, null);
}
注意代码中我标注为绿色的一个StatusBarService.addPendingOp方法,它会创建一个PendingOp对象op,然后
设置op.code=OP_ADD_ICON,然后交给StatusBarService.mHandler去处理,我们根据OP_ADD_ICON搜索mHandler.handleMessage方法:
//前面函数逻辑我省略了
if (doOp) {
switch (op.code) {
case OP_ADD_ICON:
case OP_UPDATE_ICON:
performAddUpdateIcon(op.key, op.iconData, op.notificationData);
break;
根据op.code==OP_ADD_ICON,线程会调用performAddUpdateIcon方法:
void performAddUpdateIcon(IBinder key, IconData data, NotificationData n)
由于这个函数是更新状态图标和通知图标的统一入口函数,它提供了两个入口参数,IconData data, NotificationData n,当我们添加的是时间图标的时候,n==null:
void performAddUpdateIcon(IBinder key, IconData data, NotificationData n){
// n != null means to add or update notification
if (n != null) {
...
}
// to add or update icon ,this also incluce Notification icons
synchronized (mIconMap) {//mIconMap中缓存了所有显示的和不显示的通知图标和状态图标
StatusBarIcon icon = mIconMap.get(key);//first to get icon from mIconMap
if (icon == null) {//if get icon from mIconMap is null,we should to add it
// add icon
LinearLayout v = n == null ? mStatusIcons : mNotificationIcons;
//创建一个状态图标
icon = new StatusBarIcon(mContext, data, v);
mIconMap.put(key, icon);
mIconList.add(icon);//mIconList应该是显示的状态图标
if (n == null) {//n==null,说明在添加状态图标
int slotIndex = getRightIconIndex(data.slot);
StatusBarIcon[] rightIcons = mRightIcons;
if (rightIcons[slotIndex] == null) {
int pos = 0;
for (int i=mRightIcons.length-1; i>slotIndex; i--) {
StatusBarIcon ic = rightIcons[i];
if (ic != null) {
pos++;
}
}
rightIcons[slotIndex] = icon;
mStatusIcons.addView(icon.view, pos);//mStatusIcons==LinearLayout android:id="@+id/statusIcons"
} else {
Slog.e(TAG, "duplicate icon in slot " + slotIndex + "/" + data.slot);
mIconMap.remove(key);
mIconList.remove(icon);
return ;
}
} else {// is notification add notification icon
...
}
} else {//icon!=null说明是图标更新
if (n == null) {//状态图标更新
// right hand side icons -- these don't reorder
icon.update(mContext, data);
} else {//is notification to update notification icon
...
}
}
}
结合我的红色部分的注释,认真看完该函数的代码,相信你已经知道状态图标添加的详细过程。
10.通知图标的加载
我以sd卡插入状态栏通知为例进行说明。当sd 插入,代码会执行怎样的逻辑,先看一下调用堆栈:
StatusBarService.addIcon(IconData, NotificationData) line: 423
NotificationManagerService.enqueueNotificationWithTag(String, String, int, Notification, int[]) line: 745
NotificationManager.notify(String, int, Notification) line: 110
NotificationManager.notify(int, Notification) line: 90
StorageNotification.setMediaStorageNotification(int, int, int, boolean, boolean, PendingIntent) line: 478
调用进入了StatusBarService.addIcon函数,跟前面添加状态图标调用的是同一个函数,只是参数IconData==null,而
NotificationData!=null。接下来执行逻辑差不多,StatusBarService.addPendingOp方法,它会创建一个
PendingOp对象op,然后
设置op.code=OP_ADD_ICON,然后交给StatusBarService.mHandler去处理,根据op.code==OP_ADD_ICON,线程会调用performAddUpdateIcon方法:
void performAddUpdateIcon(IBinder key, IconData data, NotificationData n)
throws StatusBarException {
//省略无关代码
if (n != null) {
//这里我省略了一段逻辑了,是跟ExpandedView相关,当我们点击statusbar往下拖动会展开一个ExpandedView,这个以后再说
//不深入展开了,当收到一个通知,这里判断通知view列表中是否存在这个通知view,若不存在添加,若存在,更新
// 添加要显示的通知到队列中,tickerview会依次显示出来,效果如(图2)
if (n.tickerText != null && mStatusBarView.getWindowToken() != null
&& (oldData == null
|| oldData.tickerText == null
|| !CharSequences.equals(oldData.tickerText, n.tickerText))) {
if (0 == (mDisabled &
(StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText);//添加一个通知到tickerview
}
}
}
// to add or update icon ,this also incluce Notification icons
synchronized (mIconMap) {
StatusBarIcon icon = mIconMap.get(key);//从缓存中查看是否已存在该图标
if (icon == null) {//if get icon from mIconMap is null,we should to add it
// add
LinearLayout v = n == null ? mStatusIcons : mNotificationIcons;
icon = new StatusBarIcon(mContext, data, v);
mIconMap.put(key, icon);
mIconList.add(icon);
if (n == null) {
//状态图标处理
} else {// is notification add notification icon
//添加左边通知图标,如sd卡,usb图标等,mNotificationIcons=IconMerger android:id="@+id/notificationIcons"
int iconIndex = mNotificationData.getIconIndex(n);
mNotificationIcons.addView(icon.view, iconIndex);
}
} else {
if (n == null) {
// right hand side icons -- these don't reorder
icon.update(mContext, data);
} else {//is notification to update notification icon
//更新通知图标
// remove old
ViewGroup parent = (ViewGroup)icon.view.getParent();
parent.removeView(icon.view);
// add new
icon.update(mContext, data);
int iconIndex = mNotificationData.getIconIndex(n);
mNotificationIcons.addView(icon.view, iconIndex);
}
}
}
}
11.状态图标更新
--1.通过广播接收器的方式
当StatusBarPolicy被构建的时候,会注册一个广播消息接收器mIntentReceiver:
IntentFilter filter = new IntentFilter();
// Register for Intent broadcasts for...
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIME_CHANGED);
......
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
一旦接收到状态图标变化消息,就会通知StatusBarService去变更状态图标,还是以状态栏的时钟为例:
if (action.equals(Intent.ACTION_TIME_TICK)) {
updateClock();//此处接收时间变化消息
}
else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
updateClock();//此处接收时间变更消息
-->updateClock()
-->StatusBarService.updateIcon(IBinder key, IconData data, NotificationData n)
-->addPendingOp(OP_UPDATE_ICON, key, data, n, -1);
}
这个跟前面添加状态图标调用是一致的,只是addIcon中addPendingOp(OP_UPDATE_ICON, key, data, n, -1);操作是 OP_UPDATE_ICON,所以,同样,
方法会执行到performAddUpdateIcon,后面逻辑见前面讲述的状态图标更新。
--2.通过远程代理方式
StatusBarManager有一个更新图标的方法: public void updateIcon(IBinder key, String slot, int iconId, int iconLevel),不过StatusBarManager并未把方法公开在sdk中,但是应该有方法可以访问的,
像launcher就有访问framework中未公开在sdk中的方法,如何实现这里我不作讨论。
//////////////////StatusBarManager.updateIcon//////////////////////////////
public void updateIcon(IBinder key, String slot, int iconId, int iconLevel) {
try {
mService.updateIcon(key, slot, mContext.getPackageName(), iconId, iconLevel);
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
}
}
mService是StatusBarManager的一个成员变量,StatusBarManager被构建的时候被赋值,他是IStatusBar的一个代理对象
StatusBarManager(Context context) {
mContext = context;
//
mService = IStatusBar.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}