Android 10.0 顶部状态栏系统图标显示分析
学习笔记:SystemUI StatusBar 手机信号相关图标的显示和更新流程分析
这里StatuBar的创建就不说了,前面已经说过了。
SystemUI中StatusBar的图标控制器实现类为StatusBarIconControllerImpl,其继承了StatusBarIconController的接口,用于跟踪所有图标的状态,并将对应的状态发送给注册的图标管理器(IconManagers)。当我们在StatusBar中获取到它的实例后,还会将它传给PhoneStatusBarPolicy和StatusBarSignalPolicy对象。PhoneStatusBarPolicy控制启动时装载哪些图标(蓝牙,定位等),而StatusBarSignalPolicy控制网络信号图标(移动网络,WiFi,以太网)的变化。
一起来看 StatuBar 的 start() 方法:
@Override
public void start() {
// 省略部分代码......
// 创建整个SystemUI视图并添加到WindowManager中
createAndAddWindows();//这个重点方法,创建相关的视图
// 省略部分代码......
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy.init();
mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
// 省略部分代码......
}
这里的 mIconPolicy 就是 PhoneStatusBarPolicy对象,mSignalPolicy 就是 StatusBarSignalPolicy 对象。我们这里以 StatusBarSignalPolicy 为例去研究。
StatusBarSignalPolicy实现了NetworkControllerImpl.SignalCallback接口,SignalCallback接口定义在NetworkControllerImpl实现的接口NetworkController中。
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@Inject
public NetworkControllerImpl(Context context, @Background Looper bgLooper,
DeviceProvisionedController deviceProvisionedController,
BroadcastDispatcher broadcastDispatcher, ConnectivityManager connectivityManager,
TelephonyManager telephonyManager, WifiManager wifiManager,
NetworkScoreManager networkScoreManager) {
this(context, connectivityManager,
telephonyManager,
wifiManager,
networkScoreManager,
SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
new CallbackHandler(),
new AccessPointControllerImpl(context),
new DataUsageController(context),
new SubscriptionDefaults(),
deviceProvisionedController,
broadcastDispatcher);
mReceiverHandler.post(mRegisterListeners);
}
private final Runnable mRegisterListeners = new Runnable() {
@Override
public void run() {
registerListeners();
}
};
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);
mPhone.listen(mPhoneStateListener, LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
// broadcasts
IntentFilter filter = new IntentFilter();
// wifi相关
// wifi信号强度广播
filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
// wifi状态变化广播
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
// wifi连接状态改变
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
// 移动网络相关
// SIM卡状态改变
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(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
mContext.registerReceiver(this, filter, null, mReceiverHandler);
mListening = true;
// 省略部分代码......
// 4.更新移动网络控制器
updateMobileControllers();
}
在NetworkControllerImpl 的构造方法里,最终会调用到:registerListeners() 方法进行广播的注册。
广播处理:
@Override
public void onReceive(Context context, Intent intent) {
if (CHATTY) {
Log.d(TAG, "onReceive: intent=" + intent);
}
final String action = intent.getAction();
switch (action) {
case ConnectivityManager.CONNECTIVITY_ACTION:
case ConnectivityManager.INET_CONDITION_ACTION:
// 省略部分代码......
break;
case Intent.ACTION_AIRPLANE_MODE_CHANGED:
// 省略部分代码......
break;
case TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
// We are using different subs now, we might be able to make calls.
// 省略部分代码......
break;
case TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
// Notify every MobileSignalController so they can know whether they are the
// data sim or not.
// 省略部分代码......
break;
case Intent.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
// 省略部分代码......
break;
case Intent.ACTION_SERVICE_STATE:
// 省略部分代码......
break;
case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
// 省略部分代码......
break;
case ImsManager.ACTION_IMS_SERVICE_UP:
case ImsManager.ACTION_IMS_SERVICE_DOWN:
// 省略部分代码......
break;
case ACTION_HIGH_DEF_AUDIO_SUPPORT:
// 省略部分代码......
break;
case ACTION_MODEM_CHANGE:
// 省略部分代码......
break;
default:
int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (SubscriptionManager.isValidSubscriptionId(subId)) {
if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
mMobileSignalControllers.get(subId).handleBroadcast(intent);
} else {
// Can't find this subscription... We must be out of date.
updateMobileControllers();
}
} else {
// wifi状态图标处理
// No sub id, must be for the wifi.
mWifiSignalController.handleBroadcast(intent);
}
break;
}
}
这里以 wifi状态图标处理 为例;接下来看WifiSignalController#handleBroadcast():
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
public void handleBroadcast(Intent intent) {
mWifiTracker.handleBroadcast(intent);
mCurrentState.enabled = mWifiTracker.enabled;
mCurrentState.connected = mWifiTracker.connected;
mCurrentState.ssid = mWifiTracker.ssid;
mCurrentState.rssi = mWifiTracker.rssi;
mCurrentState.level = mWifiTracker.level;
mCurrentState.statusLabel = mWifiTracker.statusLabel;
notifyListenersIfNecessary();
}
在WifiSignalController#handleBroadcast()方法中,就两个实现,一个是获取 WiFi 的状态,一个是通知更新状态。
我们直接看通知SignalController# notifyListenersIfNecessary() :
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
public void notifyListenersIfNecessary() {
if (isDirty()) {
saveLastState(); // 保持此时的状态
notifyListeners(); // 通知监听器
}
}
public final void notifyListeners() {
notifyListeners(mCallbackHandler);
}
public abstract void notifyListeners(SignalCallback callback);
notifyListener()方法的实现在WifiSignalController类中:
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@Override
public void notifyListeners(SignalCallback callback) {
// only show wifi in the cluster if connected or if wifi-only
boolean visibleWhenEnabled = mContext.getResources().getBoolean(
R.bool.config_showWifiIndicatorWhenEnabled);
boolean wifiVisible = mCurrentState.enabled && (
(mCurrentState.connected && mCurrentState.inetCondition == 1)
|| !mHasMobileDataFeature || mWifiTracker.isDefaultNetwork
|| visibleWhenEnabled);
String wifiDesc = mCurrentState.connected ? mCurrentState.ssid : null;
boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
String contentDescription = getTextIfExists(getContentDescription()).toString();
if (mCurrentState.inetCondition == 0) {
contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet));
}
IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
IconState qsIcon = new IconState(mCurrentState.connected,
mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
: getQsCurrentIconId(), contentDescription);
// callback为 CallbackHandler对象
callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut,
wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel);
}
可以看到,这里回调了StatusBarSignalPolicy#setWifiIndicators() 方法:
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@Override
public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
boolean activityIn, boolean activityOut, String description, boolean isTransient,
String statusLabel) {
boolean visible = statusIcon.visible && !mBlockWifi;
boolean in = activityIn && mActivityEnabled && visible;
boolean out = activityOut && mActivityEnabled && visible;
WifiIconState newState = mWifiIconState.copy();
newState.visible = visible;
newState.resId = statusIcon.icon;
newState.activityIn = in;
newState.activityOut = out;
newState.slot = mSlotWifi;
newState.airplaneSpacerVisible = mIsAirplaneMode;
newState.contentDescription = statusIcon.contentDescription;
MobileIconState first = getFirstMobileState();
newState.signalSpacerVisible = first != null && first.typeId != 0;
updateWifiIconWithState(newState);
mWifiIconState = newState;
}
private void updateWifiIconWithState(WifiIconState state) {
if (state.visible && state.resId > 0) {
mIconController.setSignalIcon(mSlotWifi, state);
mIconController.setIconVisibility(mSlotWifi, true);
} else {
mIconController.setIconVisibility(mSlotWifi, false);
}
}
通过StatusBarIconController接口设置图标的套路都是一样的:
- 获取图标名字
- 监听事件
- 通过StatusBarIconControllerImpl相应的方法设置图标。
接下来再看StatusBarIconControllerImpl#setSignalIcon():
/**
* Signal icons need to be handled differently, because they can be
* composite views
*/
@Override
public void setSignalIcon(String slot, WifiIconState state) {
int index = getSlotIndex(slot);
if (state == null) {
removeIcon(index, 0);
return;
}
StatusBarIconHolder holder = getIcon(index, 0);
if (holder == null) {
holder = StatusBarIconHolder.fromWifiIconState(state);
setIcon(index, holder);
} else {
holder.setWifiState(state);
handleSet(index, holder);
}
}
首先设置WiFi的状态信息,遍历mIconGroups分别执行StatusBarIconController接口中静态类IconManager中的onIconAdded()和onSetIconHolder()的回调。
IconManager用于将信息从StatusBarIconController转换为ViewGroup中的ImageViews(com.android.systemui.statusbar.AlphaOptimizedImageView)。
接着看IconManager中的onIconAdded()和onSetIconHolder()方法:这两个方法一个用于添加、一个用于更新。
public interface StatusBarIconController {
....
public static class DarkIconManager extends IconManager {
....
public DarkIconManager(LinearLayout linearLayout) {
// 将布局传入IconManager
super(linearLayout);
mIconHPadding = mContext.getResources().getDimensionPixelSize(
R.dimen.status_bar_icon_padding);
mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
}
....
@Override
protected void onIconAdded(int index, String slot, boolean blocked,
StatusBarIconHolder holder) {
// 调用到父类的addHolder方法
StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
....
}
}
public static class IconManager implements DemoMode {
....
protected final ViewGroup mGroup;
protected final Context mContext;
public IconManager(ViewGroup group) {
mGroup = group;
mContext = group.getContext();
mIconSize = mContext.getResources().getDimensionPixelSize(
R.dimen.status_bar_height);
....
}
....
protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,
StatusBarIconHolder holder) {
switch (holder.getType()) {
case TYPE_ICON:
return addIcon(index, slot, blocked, holder.getIcon());
case TYPE_WIFI:
return addSignalIcon(index, slot, holder.getWifiState());
case TYPE_MOBILE:
return addMobileIcon(index, slot, holder.getMobileState());
}
return null;
}
@VisibleForTesting
protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) {
// 创建一个StatusBarWifiView
StatusBarWifiView view = onCreateStatusBarWifiView(slot);
view.applyWifiState(state);
// 将view 添加进ViewGroup
mGroup.addView(view, index, onCreateLayoutParams());
if (mIsInDemoMode) {
mDemoStatusIcons.addDemoWifiView(state);
}
return view;
}
private StatusBarWifiView onCreateStatusBarWifiView(String slot) {
StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, slot);
return view;
}
....
public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) {
switch (holder.getType()) {
case TYPE_ICON:
onSetIcon(viewIndex, holder.getIcon());
return;
case TYPE_WIFI:
onSetSignalIcon(viewIndex, holder.getWifiState());
return;
case TYPE_MOBILE:
onSetMobileIcon(viewIndex, holder.getMobileState());
default:
break;
}
}
public void onSetSignalIcon(int viewIndex, WifiIconState state) {
StatusBarWifiView wifiView = (StatusBarWifiView) mGroup.getChildAt(viewIndex);
if (wifiView != null) {
wifiView.applyWifiState(state);
}
if (mIsInDemoMode) {
mDemoStatusIcons.updateWifiState(state);
}
}
....
}
}
这里根据不同的StatusBarIconHolder类型,设置不同的网络Icon,上面列出了 Wifi 图标相关的方法。
SystemUI状态栏图标根据源码可大体分为三种:
1. StatusBarIconView
2. StatusBarWifiView
3. StatusBarMobileView
这里主要以Wifi 相关图标(StatusBarWifiView)进行分析,添加Icon时首先会创建一个
StatusBarWifiView,然后调用StatusBarWifiView的applyWifiState更新其显示状态,最后将其加入到CollapsedStatusBarFragment中放置Icon的ViewGroup中,这样就完成了添加过程;
再来看看 CollapsedStatusBarFragment:
// SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks {
....
private DarkIconManager mDarkIconManager;
....
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.status_bar, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
....
// 这里可以看出status_bar布局中的statusIcons就是我们展示各种Icon的区域
mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));
mDarkIconManager.setShouldLog(true);
Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
....
}
}
补充:
notifyListenersIfNecessary()在其父类SignalController中定义,
// SignalController.java
private final CallbackHandler mCallbackHandler;
public void notifyListenersIfNecessary() {
if (isDirty()) {
saveLastState();
notifyListeners();
}
}
// 在这里注意了,在这里的的参数是 CallbackHandler 的对象
public final void notifyListeners() {
notifyListeners(mCallbackHandler);
}
// callback 则是 CallbackHandler 的对象。
public abstract void notifyListeners(SignalCallback callback);
CallbackHandler维护了所有需要监听的SignalCallback接口对象,我们的StatusBarSignalPolicy就实现了该接口。
StatusBarSignalPolicy主要执行网络图标的刷新动作,其实现了NetworkControllerImpl.SignalCallback接口,然后注册到NetworkController,其具体实现类NetworkControllerImpl会根据WIFI,SIM等状态广播来进一步派发给具体的Controller,例如WifiSignalController,每个Controller只与CallbackHandler交互,然后CallbackHandler继续转交给维护的SignalCallback接口的具体实现类,例如StatusBarSignalPolicy