状态栏的功能是以图标的简洁方式将设备的状态反馈在手机顶部的一小块条形区域,状态栏的功能可参考上篇状态栏的功能介绍。我们可以根据SystemUI的功能来猜想状态栏的运行方式,状态栏需要实时反馈系统状态,那么它就会长存系统中,在android中能长存系统运行的组件只有service,对,状态栏就是一个长存系统进程空间运行的Service,它是一个系统服务。在android系统启动过程中,当ActivityManager这些系统服务启动完成后,在SystemServer中会启动SystemUIService(也就是状态栏服务),将状态栏服务在系统进程中长期运行。状态栏的代码在Android源码的路径是\frameworks\base\package\SystemUI, 我们看看SystemUI的类结构:
从类图中我们可以看到状态栏的核心类是StatusBar,它是一个抽象类,它的start方法定义了状态栏启动时的具体步骤。StatusBar继承自SystemUI,SystemUI是SystemUIService的子类(frameworks\base\packages\SystemUI\src\com\android\systemui),SystemUIService继承Service,所以StatusBar是一个Service。 StatusBar(frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar)实现了CommandQueue.CallBack接口,CommandQueue继承自IStatusBar.stub远程接口, 在CommandQueue中IStatusBar.stub接口的方法通过CommandQueue的Callback接口实现,StatusBar又是IStatusBar.stub远程接口的实现类。 因为StatusBar是抽象类,所以IStatusBar.stub接口方法的实现在StatusBar的两个子类PhoneStatusBar和TableteStatusBar中,这两个子类分别是手机和平板的状态栏操控类。
下面我们来看看SystemUI是怎么启动的。先看时序图:
一,我们知道Android的启动过程第二个init中是启动系统的框架服务,如我们熟悉的ActivityManagerService,PackageManagerService等,在这些服务启动完成后,SystemServer会启动SystemUI服务。在SystemServer中调用了startSystemUI()方法,此方法中通过context.start方法开始启动SystemUIService。
二,start启动service时会调用SystemUIService的onCreate()方法创建service
public void onCreate() {
// Pick status bar or system bar.
IWindowManager wm = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
try {
SERVICES[0] = 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();
}
}
在创建Service时做了两步操作,一是判断设备是手机还是平板,确定具体的状态栏操控类。判断要使用的装态栏是通过wm.canStatusBarHide()获取,config_statusBarComponent的值是com.android.systemui.statusbar.phone.PhoneStatusBar,这是手机的状态栏,config_systemBarComponent的值是com.android.systemui.statusbar.tablet.TabletStatusBar,定义位于(frameworks\base\packages\SystemUI\res\values\),这是平板的状态栏。二是调用已确定SystemUI的start方法启动状态栏。
我们此处拿手机举例,所以下一步会调PhoneStatusBar的start方法。
三,调用PhoneStatusBar的start方法,此方法的主要操作是调用父类的start方法,因为状态栏的具体启动步骤在StatusBar的start方法中,这样也方便规定平板和手机按照同样的步骤启动状态栏,至于两种状态栏在操作中的不同都可以又平板后手机具体的状态栏实现类来实现。
@Override
public void start() {
mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
mWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
super.start();
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy = new PhoneStatusBarPolicy(mContext);
}
四,父类StatusBar的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.
}
…………
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,
PixelFormat.OPAQUE);
// the status bar should be in an overlay if possible
final Display defaultDisplay
= ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
if (ActivityManager.isHighEndGfx(defaultDisplay)) {
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
lp.gravity = getStatusBarGravity();
lp.setTitle("StatusBar");
lp.packageName = mContext.getPackageName();
lp.windowAnimations = R.style.Animation_StatusBar;
WindowManagerImpl.getDefault().addView(sb, lp);
……
}
五,在StatusBar的start方法中首先会调用
// First set up our views and stuff.
View sb = makeStatusBarView();
这是一个抽象方法,在子类中实现,通过不同子类实现此方法,刚好实现平板和手机状态栏View的不同构造。这里我们启动的是手机,所以实现此方法的子类是phoneStatusBar.java。在PhoneStatusBar的makeStatusBar方法中除了构造手机状态栏的view还注册了一批系统的监听,用来监听系统的状态,这些监听都在每一种状态图标的控制类中初始化时完成注册。
protected View makeStatusBarView() {
…………
//初始化扩展状态栏,就是状态栏下拉后的那个View
ExpandedView expanded = (ExpandedView)View.inflate(context,
R.layout.status_bar_expanded, null);
expanded.mService = this;
…………
//获得状态栏的View
PhoneStatusBarView sb = (PhoneStatusBarView)View.inflate(context,
R.layout.status_bar, null);
sb.mService = this;
mStatusBarView = sb;
//初始化状态栏中的子View
// figure out which pixel-format to use for the status bar.
mPixelFormat = PixelFormat.OPAQUE;
mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
mIcons = (LinearLayout)sb.findViewById(R.id.icons);
mTickerView = sb.findViewById(R.id.ticker);
…………
//初始化位置,电量,网络,信号的控制类
// Other icons
mLocationController = new LocationController(mContext); // will post a notification
mBatteryController = new BatteryController(mContext);
mBatteryController.addIconView((ImageView)sb.findViewById(R.id.battery));
mNetworkController = new NetworkController(mContext);
final SignalClusterView signalCluster =
(SignalClusterView)sb.findViewById(R.id.signal_cluster);
mNetworkController.addSignalCluster(signalCluster);
signalCluster.setNetworkController(mNetworkController);
//启动保存近期打开任务的线程,开始在后台运行
// Recents Panel
mRecentTasksLoader = new RecentTasksLoader(context);
updateRecentsPanel();
return sb;
}
六,状态栏的View构造完成后,将sb的View返回给了父类StatusBar。接着会定义一些iconList ,notificationKeys,notifications容器,用来保存系统产生的状态图标StatusBarIcon和通知notification。还实例化了CommandQueue对象,在类的结构图那我们已说过,CommandQueue是一个IStatusBar.stub远程接口,用自身定义的CallBack接口实现IStatusBar.stub远程接口中的方法。而Callback接口是在StatusBar的子类中实现的,所以在实例化CommandQueue时需要将StatusBar的子类对象传入,在CommandQueue中保存,用来实现IStatusBar.stub远程接口方法。
//Statusbar中初始化传入的值
mCommandQueue = new CommandQueue(this, iconList);
//CommandQueue的构造方法
public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
mCallbacks = callbacks;
mList = list;
}
七,注册StatusBar到StatusBarManagerService中。
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.
}
StatusBarManagerService.java在framework的service包下面,是一个系统服务。具体注册方法大家可以到它的源码中去看,此处不再贴代码。
在注册StatusBar时,主要是将我们上步实例化好的mCommandQueue对象付给StatusbarManagerService中的mBar属性,其他应用需要操作状态栏时,就可以获取StatusBarManagerService的远程代理IStatusBarService,通过AIDL进程间通信,调用StatusBarManagerService的方法,StatusBarManagerService中方法对状态栏的操作再通过mBar跨进程传给我们启动的PhoneStatusBar对象中。
注册成功之后,会调用WindowManagerImpl.getDefault().addView(sb, lp);将构造好的状态栏View添加到窗口上,这时我们就可以看见手机顶上的那条状态栏了。
八,启动PowerUI,它是集中了对电源状态的一些处理提示,继承于SystemUI。在构造SystemUIService时,SERVICES[]的初始值是
final Object[] SERVICES = new Object[] {
0, // system bar or status bar, filled in below.
com.android.systemui.power.PowerUI.class,
};
所以再启动完StatusBar的子类后,会启动PowerUI.java。PowerUI的start方法中注册了和电池相关的广播,在相应广播发生后,PowerUI会通过提示框提醒用户。
这样,SystemUIService就构造完成,开始运行于系统进程中,当手机状态发生改变时,在启动过程中注册的监听和广播捕捉到相关信息,就会更新状态栏上通标,以简单的方式通知用户。