ps:Android4.2
首先介绍什么是SystemUI:
对于Phone来说SystemUI指的是:StatusBar、NavigationBar.而对于平板或是TV来说SystemUI指的是CombineBar.
SystemUI也就是我们Phone的信号、蓝牙、WIFI标志等等这些状态,当我们的设备开机后首先呈现给用户的就是各种界面(包括SystemUI)。
下面介绍SystemUI的启动流程:
首先来看看SystemUI的代码结构,如下图:
在android4.2中,谷歌整合了phone和平板(TV)的SystemUI,也就是说可以根据设备的类型可以自动匹配相关的SystemUI。
分析应用程序我们一般从AndroidManifest.xml开始,SystemUI也是如此,我们打开AndroidManifest,有如下相关代码:
我们发现SystemUIService,他是在一开机就启动的服务。而SystemUIService是在SystemServer.java中真正启动的,如下代码:
static final void startSystemUi(Context context) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.OWNER);
}
而这里的startSystemUi是在android系统启动过程中的ServerThread的run方法中调用的。在SystemServer中,初始化了Android系统中的Java层服务,如PowerManagerService、WindowManagerService等等,当然也包括了SystemUIService,它们通过ServiceManager的addService()方法,添加到ServiceManager的管理中。而我们要关注的就是StatusBarManagerService 代码如下:
...
StatusBarManagerService statusBar = null;
...
try {
Slog.i(TAG, "Status Bar");
statusBar = new StatusBarManagerService(context, wm);
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
} catch (Throwable e) {
reportWtf("starting StatusBarManagerService", e);
}
...
try {
Slog.i(TAG, "Notification Manager");
notification = new NotificationManagerService(context, statusBar, lights);
ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification);
networkPolicy.bindNotificationManager(notification);
} catch (Throwable e) {
reportWtf("starting Notification Manager", e);
}
...
既然到这里SystemUIService已经启动,那么我们就继续跟踪该Service吧。在SystemUIService的onCreate中:
@Override
public void onCreate() {
// Tell the accessibility layer that this process will
// run as the current user, i.e. run across users.
AccessibilityManager.createAsSharedAcrossUsers(this);
// Pick status bar or system bar.
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
try {
SERVICES[0] = wm.hasSystemNavBar()
? R.string.config_systemBarComponent
: R.string.config_statusBarComponent;
} 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();
}
}
在这段代码中,通过AIDL的方式获取了WindowManager的对象wm,并调用其方法hasSystemNavBar()来判断当前设备的类型,也就是说如果我们使用的Phone那么后续就会加载StatusBar和NivagationBar;而如果我们设备类型是Tablet(TV)之类的(可以在配置文档里面配置),就会加载CombiedBar。而
对于平板 R.string.config_systemBarComponent 在config.xml中的值为:com.android.systemui.statusbar.tablet.TabletStatusBar ,我们继续跟踪,在TabletStatusBar中根据函数名我们可以判断在makeStatusBarView() 中对statusbar进行绘制。见代码:
........
final TabletStatusBarView sb = (TabletStatusBarView)View.inflate( context, R.layout.system_bar, null);
mBarContents = (ViewGroup) sb.findViewById(R.id.bar_contents);
.........
mNotificationArea = sb.findViewById(R.id.notificationArea);
mBackButton = (ImageView)sb.findViewById(R.id.back);
mNavigationArea = (ViewGroup) sb.findViewById(R.id.navigationArea);
mHomeButton = mNavigationArea.findViewById(R.id.home);
mMenuButton = mNavigationArea.findViewById(R.id.menu);
mVolumeDownButton = mNavigationArea.findViewById(R.id.volume_down);
mVolumeUpButton = mNavigationArea.findViewById(R.id.volume_up);
mRecentButton = mNavigationArea.findViewById(R.id.recent_apps);
......
由此可以看出状态栏所有相关的东东在这里都体现出来了。
下面我们进入布局system_bar中看看:
<!-- notification icons & panel access -->
<include layout="@layout/system_bar_notification_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_marginTop="1dp"
/>
而在system_bar_notification_area中有:
<LinearLayout
android:id="@+id/notificationTrigger"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
>
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:singleLine="true"
android:paddingLeft="6dip"
android:layout_marginRight="8dip"
android:gravity="center_vertical|left"
/>
对此我们进入到com.android.systemui.statusbar.policy.Clock中进一步查看:
final void updateClock() {
mCalendar.setTimeInMillis(System.currentTimeMillis());
setText(getSmallTime());
}
private final CharSequence getSmallTime() {
Context context = getContext();
boolean b24 = DateFormat.is24HourFormat(context);
int res;
if (b24) {
res = R.string.twenty_four_hour_time_format;
} else {
res = R.string.twelve_hour_time_format;
}
...
String result = sdf.format(mCalendar.getTime());
if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) {
int magic1 = result.indexOf(MAGIC1);
int magic2 = result.indexOf(MAGIC2);
if (magic1 >= 0 && magic2 > magic1) {
SpannableStringBuilder formatted = new SpannableStringBuilder(result);
if (AM_PM_STYLE == AM_PM_STYLE_GONE) {
formatted.delete(magic1, magic2+1);
} else {
if (AM_PM_STYLE == AM_PM_STYLE_SMALL) {
CharacterStyle style = new RelativeSizeSpan(0.7f);
formatted.setSpan(style, magic1, magic2,
Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
}
formatted.delete(magic2, magic2 + 1);
formatted.delete(magic1, magic1 + 1);
}
return formatted;
}
}
return result;
}
所以我们只要在getSmallTime中返回之前对其返回指进行处理即可达到我们的目地:比如修改显示方式等。