System UI的内容很多,包括状态栏、通知栏、锁屏、Navigation bar和Recent等;本篇主要分析状态栏中的RAT图标、数据图标以及信号格的更新。每一个新版本,Google都会对这一块进行修改,所以不同版本之间会有些差异,本篇内容是基于Android O。
本文分成三部分:
1. System UI的启动
2. System UI的布局
3. 图标的更新
1. System UI的启动
下方的流程图从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方法如下:
@Overridepublic 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() {@Overridepublic void onFragmentViewCreated(FragmentManager fm, Fragment f, View v,Bundle savedInstanceState) {FragmentHostManager.this.onFragmentViewCreated(f);}@Overridepublic void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {FragmentHostManager.this.onFragmentViewDestroyed(f);}@Overridepublic 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这套理解不深,所以不明白这个设计的初衷,是为了解耦,模块化?
Dependency.java
SystemUIApplication启动的service就包含Dependency, Dependency包含一个ArrayMap <Object, DependencyProvider>类型的成员变量mProviders, Dependency.start方法中会重要类的对象放到mProviders中, 下面是部分代码:
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));
2. System UI的布局
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"><!-- The alpha of this area is controlled from both PhoneStatusBarTransitions andPhoneStatusBar (DISABLE_NOTIFICATION_ICONS). --><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" /><!--系统icon显示区域--><com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"android:layout_width="wrap_content"android:layout_height="match_parent"android:orientation="horizontal"><!--include system_icons显示信号格,RAT等--><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"/><!--加载signal_cluster_view布局--><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方法会被调用:
@Overridepublic void setSubs(List<SubscriptionInfo> subs) {//如果已经包含了subs信息,那么直接returnif (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;}
3. 图标更新
下面的流程图简单展示了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);// broadcastsIntentFilter 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方法中。