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方法中。


结束!

SystemUI---RAT相关推荐

  1. android 9.0打开wifi,Android9.0 SystemUI 屏蔽打开wifi时不显示4G图标的逻辑

    Android 9.0 中打开wifi时,不显示4G图标,只有信号值,现项目需求在打开wifi时仍然显示4G图标. 排查及修改过程如下: systemui/src/com/android/system ...

  2. iOS7系统iLEX RAT冬青鼠安装教程:无需刷机还原纯净越狱系统

    全网科技 温馨提醒:iLEX RAT和Semi-Restore的作用都是让你的已越狱的设备恢复至越狱的初始状态. 可是要注意无论你是用iLexRAT冬青鼠还是Semi-restore.对于还原来说都存 ...

  3. Codeforces Round #341 (Div. 2) D. Rat Kwesh and Cheese 数学

    D. Rat Kwesh and Cheese 题目连接: http://www.codeforces.com/contest/621/problem/D Description Wet Shark ...

  4. rat/rats--用有理数形式表示矩阵

    [功能简介]用有理分式逼近矩阵. [语法格式] 1.[N,D]=rat(X) 返回多项分数数组N和D使得N./D在默认误差1.e-6*norm (X(:),1)内逼近X. 格式变体: [N,D]=ra ...

  5. android4.0 SystemUi系统状态栏

    android4.0系统可以运行于平板电脑和手机上面,这样对于状态栏来说,也是有不同风格的,从SystemUi的代码分类我就可以看出来,google考虑了不同情况下状态栏的显示等, 在源代码里面有这么 ...

  6. android 监听时钟变化,Android4.4 SystemUI分析之Clock时钟显示

    SystemUI上的时间显示只要就在/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock ...

  7. Android 7.0 SystemUI 之启动和状态栏和导航栏简介

    Android 7.0 SystemUI 之启动和状态栏和导航栏简介 一.SystemUI 是什么 首先SystemUI 是一个系统应用,apk路径位于/system/priv-app 源码路径位于: ...

  8. C++rat maze老鼠迷宫算法(附完整源码)

    rat maze老鼠迷宫的算法 rat maze老鼠迷宫的算法的完整源码(定义,实现,main函数测试) rat maze老鼠迷宫的算法的完整源码(定义,实现,main函数测试) #include & ...

  9. [译] APT分析报告:10.Lazarus以ThreatNeedle家族攻击工业事件还原(BMP图片隐藏RAT)

    这是作者新开的一个专栏,主要翻译国外知名安全厂商的APT报告,了解它们的安全技术,学习它们溯源APT组织和恶意代码分析的方法,希望对您有所帮助.当然,由于作者英语有限,会借助机翻进行校验,还请包涵!前 ...

  10. android uid systemui,(android)system ui 内存优化

    android中systemUI是作为一个设置壁纸的服务存在的.以前项目中,对systemUI做了延迟启动的优化,可以把内存从25M左右降到8M左右,可是最近一个项目用了同样的方法(延迟启动),内存却 ...

最新文章

  1. java redis 下载_redis Java源代码 redis.rar - 下载 - 搜珍网
  2. gin获取get参数
  3. Ionic3 通讯录索引的实现
  4. 全新的 Vue3 状态管理工具:Pinia
  5. c++【深度剖析shared_ptr】
  6. 猛男教你写代码_猛男程序员,鼓存储器和1960年代机器代码的取证分析
  7. 为什么学习前端的人越来越多?
  8. lambda表达式学习例子
  9. Nacos教程_4 配置讲解
  10. 检测精度大幅提升!移动端实时的NanoDet升级版NanoDet-Plus来了!
  11. 【丐中丐】废旧光驱改装激光雕刻机
  12. 红米note5系统Android11,红米Note5-MIUI11 开发版|超多功能|Xposed|ROOT|桌面布局-刷机之家...
  13. 刷题一个半月,一口气拿下腾讯、华为、Oppo 、微软7个大厂offer,字节跳动薪资涨幅60%!...
  14. win7台式机解决插入耳机没有声音以及显示麦克风未插入的解决方法
  15. UE4对接腾讯GME语音服务(实时语音一)
  16. 分享几个影视采集网站
  17. Java系统答辩提问问题_宿舍管理系统答辩问题总结
  18. python学习-reshape()函数理解
  19. 2022华为软挑比赛(初赛笔记)
  20. 图片打开太暗看不清并且手机传给电脑的视频打开卡

热门文章

  1. 国内媒体宣传投放过程中要关注到和第三方机构合作
  2. 紫书时时阅,茶香几时成——铁观音茶香Muffin
  3. 前端实现微信公众号h5页面跳转小程序-成功案例
  4. 软件测试方法和技术,朱少民,第三章习题答案
  5. 生活随记 - 不懂就问心老人不老
  6. MATLAB仿真贝塞尔函数
  7. 【ChatGPT+XMind超级详细的保姆级思维导图教程】
  8. (附源码)ssm+mysql+基于微信平台的牙科就诊信息管理系统的设计与实现 毕业设计211157
  9. 工信部《VR产业白皮书》全文 14000字的官方解读
  10. implode 和 explode