在上一篇文章:Android SystemUI 状态栏——系统状态图标显示与管理中, 我们介绍的都是意图内限定的系统图标,我们前面说过,对于信号、电量、系统时钟都不是属于系统图标区域,下面我们来看这三个的更新和显示。 回到前面所说的makeStatusBarView函数中:
protected PhoneStatusBarView makeStatusBarView() {
......
mClock = (Clock) mStatusBarView.findViewById(R.id.clock);
......
initSignalCluster(mStatusBarView);
......
mBatteryClusterView = (BatteryClusterView) mStatusBarView.findViewById(R.id.battery_cluster_view);
mBatteryClusterView.setBatteryController(mBatteryController);
return mStatusBarView;
}
首先来看Clock,其实现类是继承自TextView的Clock类,Clock的显示即在makeStatusBarView中加载,更新的话就是拦截相关intent然后重新设置Text,所以还是比较简单的。在onAttachedToWindow中,拦截了相关的action,如下:
protected void onAttachedToWindow() {
......
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_SCREEN_ON);
......
}
接下来来看信号的显示与更新,首先来看initSignalCluster函数:
protected void initSignalCluster(View containerView) {
SignalClusterView signalCluster =
(SignalClusterView) containerView.findViewById(R.id.signal_cluster);
if (signalCluster != null) {
signalCluster.setSecurityController(mSecurityController);
signalCluster.setNetworkController(mNetworkController);
}
}
可以看到,注册了SecurityController和NetworkController的实例到SignalClusterView中,其中mSecurityController是主要管理VPN的状态,而NetworkController则是管理网络的状态,后面会详细介绍这个Controller。从这里可以看出,信号图标的具体实现View为SignalClusterView,管理状态的类为NetworkController。
首先来看NetworkController的初始化,NetworkController为抽象类,具体实现类为NetworkControllerImpl:
mNetworkController = new NetworkControllerImpl(mContext, mHandlerThread.getLooper());
mNetworkController.setUserSetupComplete(mUserSetup);
- 创建NetworkControllerImpl实例:
public NetworkControllerImpl(Context context, Looper bgLooper) { this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE), (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE), (WifiManager) context.getSystemService(Context.WIFI_SERVICE), SubscriptionManager.from(context), Config.readConfig(context), bgLooper, new CallbackHandler(), new AccessPointControllerImpl(context, bgLooper), new DataUsageController(context), new SubscriptionDefaults()); mReceiverHandler.post(mRegisterListeners); initialiseNoSimSlot(); }
调用自己的多参数构造函数,在多参数构造函数中,创建和初始化了一系列和网络状态相关的管理类(TelephonyManager、ConnectivityManager、SubscriptionManager、WifiSignalController、MobileSignalController等等),同时还有一个回调处理类——CallbackHandler,该类继承Handler并实现了EmergencyListener和SignalCallback,该回调用于处理状态更新时回调具体的View更新。 Handler执行registerListeners,拦截和网络状态相关的Action:
private void registerListeners() { for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { 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); filter.addAction(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED); if (CustomizationItem.SHOW_FEMTOCELL) { filter.addAction(ACTION_FEMTOCELL_STATE_CHANGED); } //Support for cdma two signal icon filter.addAction(PhoneConstants.ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED); mContext.registerReceiver(this, filter, null, mReceiverHandler); mListening = true; updateMobileControllers(); }
在这个注册函数中,还有一个比较重要的函数:updateMobileControllers,其调用的是doUpdateMobileControllers:
void doUpdateMobileControllers() {
List<SubscriptionInfo> subscriptions = mSubscriptionManager.getActiveSubscriptionInfoList();
if (subscriptions == null) {
subscriptions = Collections.emptyList();
}
// If there have been no relevant changes to any of the subscriptions, we can leave as is.
if (hasCorrectMobileControllers(subscriptions)) {
// Even if the controllers are correct, make sure we have the right no sims state.
// Such as on boot, don't need any controllers, because there are no sims,
// but we still need to update the no sim state.
if (DEBUG) Log.d(TAG, "hasCorrectMobileControllers true");
updateNoSims();
return;
}
setCurrentSubscriptions(subscriptions);
updateNoSims();
recalculateEmergency();
}
主要做了如下事情:
- 通过SubscriptionManager获取Sim卡对应的SubscriptionInfo
- 调用setCurrentSubscriptions函数创建或更新对应Sim卡的MobileSignalController
每一张卡都会对应由一个MobileSignalController实例管理其状态
- 更新无卡信号图标和紧急拨号网络字符
来看setCurrentSubscriptions的具体实现:
void setCurrentSubscriptions(List<SubscriptionInfo> subscriptions) {
......
mCurrentSubscriptions = subscriptions;
HashMap<Integer, MobileSignalController> cachedControllers =
new HashMap<Integer, MobileSignalController>(mMobileSignalControllers);
mMobileSignalControllers.clear();
final int num = subscriptions.size();
for (int i = 0; i < num; i++) {
int subId = subscriptions.get(i).getSubscriptionId();
// If we have a copy of this controller already reuse it, otherwise make a new one.
if (cachedControllers.containsKey(subId)) {
mMobileSignalControllers.put(subId, cachedControllers.remove(subId));
} else {
MobileSignalController controller = new MobileSignalController(mContext, mConfig,
mHasMobileDataFeature, mPhone, mCallbackHandler,
this, subscriptions.get(i), mSubDefaults, mReceiverHandler.getLooper());
controller.setUserSetupComplete(mUserSetup);
mMobileSignalControllers.put(subId, controller);
if (subscriptions.get(i).getSimSlotIndex() == 0) {
mDefaultSignalController = controller;
}
if (mListening) {
controller.registerListener();
}
}
}
if (mListening) {
for (Integer key : cachedControllers.keySet()) {
if (cachedControllers.get(key) == mDefaultSignalController) {
mDefaultSignalController = null;
}
cachedControllers.get(key).unregisterListener();
}
}
mCallbackHandler.setSubs(subscriptions);
notifyAllListeners();
......
}
下面来看MobileSignalController的具体初始化:
public MobileSignalController(Context context, Config config, boolean hasMobileData,
TelephonyManager phone, CallbackHandler callbackHandler,
NetworkControllerImpl networkController, SubscriptionInfo info,
SubscriptionDefaults defaults, Looper receiverLooper) {
super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
networkController);
mNetworkToIconLookup = new SparseArray<>();
mConfig = config;
mPhone = phone;
mDefaults = defaults;
mSubscriptionInfo = info;
mPhoneStateListener = new MobilePhoneStateListener(info.getSubscriptionId(),
receiverLooper);
mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator);
mNetworkNameDefault = getStringIfExists(
SystemUiUtil.getIdentifier(context, "string", "lockscreen_carrier_default"));
mapIconSets();
String networkName = info.getCarrierName() != null ? info.getCarrierName().toString()
: mNetworkNameDefault;
mLastState.networkName = mCurrentState.networkName = networkName;
mLastState.networkNameData = mCurrentState.networkNameData = networkName;
mLastState.enabled = mCurrentState.enabled = hasMobileData;
mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons;
// Get initial data sim state.
updateDataSim();
}
初始化中主要干了如下事情:
- 初始化和sim卡相关的参数
- 初始化MobilePhoneStateListener实例,该对象继承自PhoneStateListener,用于监听sim卡主要的状态变化,通过registerListener注册相关的事件监听
主要注册了如下监听: 1、onSignalStrengthsChanged 2、onServiceStateChanged 3、onDataConnectionStateChanged 4、onDataActivity 5、onCarrierNetworkChange
- mapIconSets初始化对应网络类型的图标
- 更新保存最近一次的网络状态
跟信号状态相关的类都已经初始化以后,我们来看是怎么具体显示和更新的,来看SignalClusterView的onAttachedToWindow函数:
protected void onAttachedToWindow() {
super.onAttachedToWindow();
......
apply();
applyIconTint();
mNC.addSignalCallback(this);
mNC.setMobileDataSettingListening(true);
}
- apply即是设置相关图标的显示
- applyIconTint对图标进行渲染
- addSignalCallback更新回调,将SignalClusterView传递给回调,这样的话,NetworkControllerImpl即可以和SignalClusterView进行通信。
然后来看具体的更新,以ACTION_SIM_STATE_CHANGED为例:
} else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
// Might have different subscriptions now.
updateMobileControllers();
updateSimState(intent);
}
所以还是回到之前setCurrentSubscriptions函数,此时是更新,非创建主要关注:
mCallbackHandler.setSubs(subscriptions);
notifyAllListeners();
由前面介绍可知mCallbackHandler最终会调用SignalClusterView,实现如下:
public void setSubs(List<SubscriptionInfo> subs) {
......
mPhoneStates.clear();
if (mMobileSignalGroup != null) {
mMobileSignalGroup.removeAllViews();
}
final int n = subs.size();
for (int i = 0; i < n; i++) {
inflatePhoneState(subs.get(i).getSubscriptionId());
}
......
}
同样的,每一张卡都对应由一个PhoneState记录状态,这里就是加载初始化。 再来看notifyAllListeners的实现:
private void notifyAllListeners() {
notifyListeners();
for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
mobileSignalController.notifyListeners();
}
mWifiSignalController.notifyListeners();
mEthernetSignalController.notifyListeners();
}
调用对应管理类的notifyListeners,在获取更新了相关的状态以后,最后都是通过回调最终到SignalClusterView中去更新。
最后总结下,如下为和信号显示即更新相关的类:
NetworkControllerImpl为信号网络的核心管理类,实现了NetworkController接口,该接口定义了四个接口,分别是SignalCallback(回调信号网络显示类SignalCluster)、EmergencyListener(紧急拨号信息的相关显示)、IconState(图标基本信息)、AccessPointController(热点信息管理)。在NetworkControllerImpl还包含了三个主要管理类:MobileSignalController(Sim信息)、WifiSignalController(wifi信息)、EthernetSignalController(有线网络),这三个管理类分别用于管理不同类型的网络状态,且都是继承于抽象类SignalController,其中由两个比较重要的类定义:IconGroup和State,分别用于保存网络图标的具体信息和状态,在子类中也继承了这两个类并丰富了内容。网络信号的具体显示类为SignalClusterView,这个类将自己注册到NetworkControllerImpl的CallBackHandler中,这样NetworkControllerImpl就可以通过调用CallBackHandler来和SignalClusterView通信并更新显示状态。在SignalClusterView包喊了一个PhoneState类,该类是SIM信息在SignalClusterView中的存储对象,更新的状态都会保存到这个类中。
下面是更新的一个时序图:
最后我们来看电量图标的显示与更新: 电池区域的布局是一块自定义view——BatteryClusterView,首先来看这块区域的加载:
mBatteryClusterView = (BatteryClusterView) mStatusBarView.findViewById(R.id.battery_cluster_view);
mBatteryClusterView.setBatteryController(mBatteryController);
所以与之相关的两个类是BatteryClusterView和BatteryController,分别为电池区域的自定义view和管理类接口。 先来看BatteryController类,这个类是一个接口,里面定义了一个回调接口BatteryStateChangeCallback,这个回调接口主要定义了一些和Battery相关状态变化回调。而这个接口则主要定义管理这个回调的方法及省电模式管理。而真正实现这些接口的类为BatteryControllerImpl,来看这个类的实现。 该类继承BroadcastReceiver并实现了BatteryController接口。在其构造函数中,注册了相关action的拦截:
private void registerReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
filter.addAction(ACTION_LEVEL_TEST);
filter.addAction(BLUETOOTH_DOCK_BATTERY_INTENT);
mContext.registerReceiver(this, filter);
}
在该类中,维护了一个BatteryStateChangeCallback的回调ArrayList,通过addStateChangedCallback和removeStateChangedCallback添加和移除,在拦截到相关的action以后,则会依次调用这些回调中的相关方法。 再来看BatteryClusterView的实现,其继承了LinerLayout,并实现了BatteryController.BatteryStateChangeCallback,先看onFinishInflate函数,主要是加载和电池相关的控件,其中比较重要的控件为BatteryMeterView类和mBatteryLevel,这两个分别为电量的图标和百分比显示。然后注册了两个监听:省电模式和电量百分比的显示。 再来看onAttachedToWindow,其将BatteryClusterView本身作为回调注册到BatteryController中:
mBatteryController.addStateChangedCallback(this);
而BatteryClusterView本身确实实现了该回调:onBatteryLevelChanged、onBluetoothDockBatteryLevelChanged、onPowerSaveChanged、onInvalidChargerChanged。所以有状态更新的时候会直接回调这几个函数。
以onBatteryLevelChanged为例,回调用updateBatteryLevelChanged:
public void onBatteryLevelChanged(int level, int chargingStatus, int packLevel, int packChargingStatus, int penLevel, int penStatus, boolean penZStyleOnly) {
if (mLevel != level || mChargingStatus != chargingStatus) {
updateBatteryLevelChanged(mBatteryView, level, chargingStatus);
updatePercent(mBatteryLevel, level, chargingStatus);
mLevel = level;
mChargingStatus = chargingStatus;
}
......
}
先来看updateBatteryLevelChanged,其调用的是batteryView.onBatteryLevelChanged方法,所以来看BatteryMeterView的实现,这个类直接继承于ImageView,其里面包含一个比较重要的对象:mDrawable(BatteryMeterDrawable)。这个类是电量的具体绘制类,是一个自定义的Drawable,电两的图标是由两部分组成:最外层的框和电量的具体显示。电量的图形绘制是通过电量多少来的,所以直接重写draw函数,根据电量的多少来绘制图形的比例。
最后来总结一下电量显示相关的类,如下类图:
BatteryControllerImpl为电量的监听管理类,其实现了BatteryController接口,其中包括注册和移除回调,设置省电模式等。BatteryClusterView为电量区域的具体实现布局,其实现了BatteryController中定义的BatteryStateChangeCallback回调,并将自己作为回调注册到BatteryControllerImpl中。当BatteryControllerImpl监听到相关状态变化的时候,会执行回调并最终调用BatteryClusterView中实现的相关方法。BatteryMeterView为电量的具体实现View,其里面有一个BatteryMeterDrawable类,是电量的具体绘制类,最终会根据具体的电量和状态绘制对应比例的图形。
更新电量时序图如下: