System UI的内容很多,包括状态栏、通知栏、锁屏、Navigation bar和Recent等;本篇主要分析状态栏中的RAT图标、数据图标以及信号格的更新。每一个新版本,Google都会对这一块进行修改,所以不同版本之间会有些差异,本篇内容是基于Android O。
本文分成三部分:
1. System UI的启动
2. System UI的布局
3. 图标的更新
下方的流程图从SystemServer启动,即SystemServer.main方法开始简单展示了StatusBar的启动流程。
SystemServer.startSystemUi方法负责去启动SystemUIService:
static final void startSystemUi(Context context, WindowManagerService windowManager) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.SYSTEM);
windowManager.onSystemUiStarted();
}
在启动SystemUIService时,SystemUIApplication会先被启动。SystemUIService的onCreate方法会调用SystemUIApplication.startServicesIfNeeded方法,后者构造了一系列SystemUI类型(子类)的对象,并调用了它们的start方法,其中就包括SystemBars,SystemBars.start方法如下:
@Override
public void start() {
if (DEBUG) Log.d(TAG, "start");
createStatusBarFromConfig();
}
private void createStatusBarFromConfig() {
if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
//"获取类信息,R.string.config_statusBarComponent的默认值是com.android.systemui.statusbar.phone.StatusBar"
final String clsName = mContext.getString(R.string.config_statusBarComponent);
if (clsName == null || clsName.length() == 0) {
throw andLog("No status bar component configured", null);
}
Class> cls = null;
try {
cls = mContext.getClassLoader().loadClass(clsName);
} catch (Throwable t) {
throw andLog("Error loading status bar component: " + clsName, t);
}
try {
mStatusBar = (SystemUI) cls.newInstance();//new一个实例
} catch (Throwable t) {
throw andLog("Error creating status bar component: " + clsName, t);
}
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mComponents;
mStatusBar.start();//调用StatusBar.start方法
if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
}
R.string.config_statusBarComponent的值是com.android.systemui.statusbar.phone.StatusBar
相比于之前的版本,Android O上没有了PhoneStatusBar.java类,取而代之的是StatusBar.java。布局文件super_status_bar.xml也发生了一些改变,不再include “status_bar.xml”,而是用CollapsedStatusBarFragment加载; CollapsedStatusBarFragment本身的加载是在StatusBar.makeStatusBarView方法中,下面即是这部分代码:
FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
setAreThereNotifications();
checkBarModes();
}).getFragmentManager()
.beginTransaction()
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit();
这段代码或许看起来不是那么清晰明了,因为里面包含了多个方法调用还夹杂了Java 8的的Lambda表达式; 其实addTagListener方法中的Lambda部分是回调,所以可以先忽略掉,只关注addTagListener方法的返回对象。
下面是FragmentHostManager.get方法的code:
public static FragmentHostManager get(View view) {
try {
return Dependency.get(FragmentService.class).getFragmentHostManager(view);
} catch (ClassCastException e) {
// TODO: Some auto handling here?
throw e;
}
}
这个方法只是调用了FragmentService的getFragmentHostManager方法,code如下:
public FragmentHostManager getFragmentHostManager(View view) {
View root = view.getRootView();
FragmentHostState state = mHosts.get(root);
if (state == null) {
state = new FragmentHostState(root);
mHosts.put(root, state);
}
return state.getFragmentHostManager();
}
FragmentService.getFragmentHostManager方法创建了一个FragmentHostState对象,然后调用这个对象的getFragmentHostManager方法,下面是FragmentHostState的相关code:
public FragmentHostState(View view) {
mView = view;
//创建一个FragmentHostManager对象
mFragmentHostManager = new FragmentHostManager(mContext, FragmentService.this, mView);
}
public FragmentHostManager getFragmentHostManager() {
return mFragmentHostManager;
}
通过上面的一些列调用,我们可以看到FragmentHostManager.get方法使用参数View创建了一个新的FragmentHostManager对象,而FragmentHostManager.addTagListener方法返回的仍是对象本身。
那么FragmentHostManager.getFragmentManager()方法返回的又是什么呢?
下面是看下FragmentHostManager的构造方法和createFragmentHost方法:
FragmentHostManager(Context context, FragmentService manager, View rootView) {
mContext = context;
mManager = manager;
mRootView = rootView;
mConfigChanges.applyNewConfig(context.getResources());
createFragmentHost(null);
}
private void createFragmentHost(Parcelable savedState) {
mFragments = FragmentController.createController(new HostCallbacks());
mFragments.attachHost(null);
mLifecycleCallbacks = new FragmentLifecycleCallbacks() {
@Override
public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v,
Bundle savedInstanceState) {
FragmentHostManager.this.onFragmentViewCreated(f);
}
@Override
public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {
FragmentHostManager.this.onFragmentViewDestroyed(f);
}
@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment f) {
Dependency.get(LeakDetector.class).trackGarbage(f);
}
};
mFragments.getFragmentManager().registerFragmentLifecycleCallbacks(mLifecycleCallbacks,
true);
if (savedState != null) {
mFragments.restoreAllState(savedState, (FragmentManagerNonConfig) null);
}
// For now just keep all fragments in the resumed state.
mFragments.dispatchCreate();
mFragments.dispatchStart();
mFragments.dispatchResume();
}
FragmentHostManager的构造函数没什么重要内容,只是调用了内部Private 方法createFragmentHost,该方法构造了一个FragmentController类型的对象mFragments,并设置了HostCallbacks对象作为回调; HostCallbacks(继承了FragmentHostCallback)内部有FragmentManagerImpl对象,FragmentManagerImpl是FragmentManager.java的内部类,继承了FragmentManager类。
下面是FragmentHostManager.getFragmentManager()的代码:
public FragmentManager getFragmentManager() {
return mFragments.getFragmentManager();
}
又调用了FragmentController的getFragmentManager()方法,对应code如下:
/**
* Returns a {@link FragmentManager} for this controller.
*/
public FragmentManager getFragmentManager() {
return mHost.getFragmentManagerImpl();
}
代码中的mHost即是HostCallbacks对象,HostCallbacks.getFragmentManagerImpl()方法返回的就是FragmentManagerImpl对象。即FragmentHostManager.getFragmentManager()方法返回了一个FragmentManagerImpl对象。再回到StatusBar.makeStatusBarView方法中:
FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
setAreThereNotifications();
checkBarModes();
}).getFragmentManager()
.beginTransaction()
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit();
FragmentManagerImpl.beginTransaction方法会返回一个BackStackRecord对象; BackStackRecord继承了FragmentTransaction, 折腾了一圈还是回到了FragmentTransaction,然后使用replace方法将CollapsedStatusBarFragment放进布局中super_status_bar.xml中; CollapsedStatusBarFragment会加载R.layout.status_bar布局(PhoneStatusBarView)。
代码流程是看懂了,但是由于对Android UI这套理解不深,所以不明白这个设计的初衷,是为了解耦,模块化?
SystemUIApplication启动的service就包含Dependency, Dependency包含一个ArrayMap
mProviders.put(NetworkController.class, () ->
new NetworkControllerImpl(mContext, getDependency(BG_LOOPER),
getDependency(DeviceProvisionedController.class)));
mProviders.put(ZenModeController.class, () ->
new ZenModeControllerImpl(mContext, getDependency(MAIN_HANDLER)));
mProviders.put(HotspotController.class, () ->
new HotspotControllerImpl(mContext));
mProviders.put(CastController.class, () ->
new CastControllerImpl(mContext));
mProviders.put(FlashlightController.class, () ->
new FlashlightControllerImpl(mContext));
System UI的布局大致分成两部分,左边用来显示通知信息,右边用来显示时钟、信号格等信息; 而且这两部分向相反方向扩展,即左边部分从右向左扩展,右边部分从左向右扩展。本文主要关注右边部分的布局,下图简单的展示了相关的布局关系:
super_status_bar.xml没有直接include “status_bar.xml”,而是用CollapsedStatusBarFragment加载到了下面的FrameLayout布局中:
<FrameLayout
android:id="@+id/status_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
status_bar.xml部分布局如下:
<LinearLayout android:id="@+id/status_bar_contents"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="6dp"
android:paddingEnd="8dp"
android:orientation="horizontal"
>
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/notification_icon_area"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal" />
<com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
>
<include layout="@layout/system_icons" />
<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:paddingStart="@dimen/status_bar_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_clock_end_padding"
android:gravity="center_vertical|start"
/>
com.android.keyguard.AlphaOptimizedLinearLayout>
LinearLayout>
system_icons.xml布局比较简单,如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/system_icons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical">
<com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/statusIcons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal"/>
<include layout="@layout/signal_cluster_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/signal_cluster_margin_start"/>
<com.android.systemui.BatteryMeterView android:id="@+id/battery"
android:layout_height="match_parent"
android:layout_width="wrap_content"
/>
LinearLayout>
signal_cluster_view.xml对应的是SignalClusterView对象,当SignalClusterView.onAttachedToWindow方法被系统调用时,SignalClusterView会调用NetworkControllerImpl.addCallback方法,在这个方法中SignalClusterView.setSubs方法会被调用:
@Override
public void setSubs(List subs) {
//如果已经包含了subs信息,那么直接return
if (hasCorrectSubs(subs)) {
return;
}
//下面两句清空了之前的数据
mPhoneStates.clear();
if (mMobileSignalGroup != null) {
mMobileSignalGroup.removeAllViews();
}
final int n = subs.size();
//根据subscription信息开始加载布局
for (int i = 0; i < n; i++) {
inflatePhoneState(subs.get(i).getSubscriptionId());
}
if (isAttachedToWindow()) {
applyIconTint();
}
}
//PhoneState是SignalClusterView的内部类,每个PhoneState对象都会加载一个mobile_signal_group.xml布局,
//所以mobile_signal_group.xml布局数量和active Subscription数量(SIM 数量)一致。
private PhoneState inflatePhoneState(int subId) {
PhoneState state = new PhoneState(subId, mContext);
if (mMobileSignalGroup != null) {
mMobileSignalGroup.addView(state.mMobileGroup);
}
mPhoneStates.add(state);
return state;
}
下面的流程图简单展示了UI更新的过程:
NetworkControllerImpl的构造函数使用mReceiverHandler.post方法将Runnable对象mRegisterListeners放到了消息队列里面,当mRegisterListeners执行的时候run方法会调用NetworkControllerImpl.registerListeners方法,这个方法监听了很多广播(NetworkControllerImpl是BroadcastReceiver的子类)。
private void registerListeners() {
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.registerListener();
}
if (mSubscriptionListener == null) {
mSubscriptionListener = new SubListener();
}
mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
// broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
mContext.registerReceiver(this, filter, null, mReceiverHandler);
mListening = true;
updateMobileControllers();
}
不同的广播,处理自然也不一样。
当收到广播ACTION_SIM_STATE_CHANGED的时候,updateMobileControllers、doUpdateMobileControllers、setCurrentSubscriptions等方法会先后被调用。setCurrentSubscriptions方法会根据最新的subscriptions信息构造MobileSignalController方法,并调用新对象的registerListener方法在TelephonyManager中设置监听:
/**
* Start listening for phone state changes.
*/
public void registerListener() {
mPhone.listen(mPhoneStateListener,
PhoneStateListener.LISTEN_SERVICE_STATE
| PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
| PhoneStateListener.LISTEN_CALL_STATE
| PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
| PhoneStateListener.LISTEN_DATA_ACTIVITY
| PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE);
mContext.getContentResolver().registerContentObserver(Global.getUriFor(Global.MOBILE_DATA),
true, mObserver);
mContext.getContentResolver().registerContentObserver(Global.getUriFor(
Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()),
true, mObserver);
}
MobileSignalController监听了service state、signal strength和call state等,当这些状态变化时MobileSignalController会通知自己的监听者,调用方法顺序是:
notifyListenersIfNecessary()
–>notifyListeners()
–>notifyListeners(mCallbackHandler)
mCallbackHandler是NetworkControllerImpl在构造MobileSignalController对象的时候传进来的,即NetworkControllerImpl.mCallbackHandler。NetworkControllerImpl.mCallbackHandler可以把状态变化通知到SignalClusterView对象,因为SignalClusterView.onAttachedToWindow方法被调用的时候,SignalClusterView调用了NetworkControllerImpl.addCallback方法在NetworkControllerImpl.mCallbackHandler中设置了监听。针对MobileSignalController的调用NetworkControllerImpl.mCallbackHandler会调用setMobileDataIndicators方法,其实这个方法只是做了赋值操作,真正更新是在apply方法中。