Android 系统 Settings 启动流程详解

2023-05-16

Settings简介

  Settings 是 Android 系统自带的一个很重要的应用,给用户提供了操作 Android 系统功能的界面。它里面包含了 Wireless & network,device,personal 以及 system 等几大块的功能设置。在 Android 源码中,该应用位于 packages/apps/Settings 下。该应用的源码是相当复杂的,设计思想很是先进,很难完全讲清楚,笔者也是读了好几遍源码再综合了几篇博客才勉强懂了Settings其启动流程的大体思路。通过博客记录下来以加深理解和印象,同时分享给大家。
  

Settings 启动流程详解

1.直接跳转子界面

  首先找到 Settings 目录,其目录结构如下,文件太多,无法完展开。
  这里写图片描述
  
这里通过每个文件夹的命名可以大概知道,每个包的大体作用是什么。由于本文主要讲解启动流程,所以先不管这些。我们先找到 Settings 的启动类,通常我们可以从清单文件中得知该应用的启动类,如下图:
这里写图片描述

从图中可以清楚的看到,Settings 的启动类为 Settings。从 Settings 源码中我们找到了Settings.java文件。但是,打开这个文件后,会感到了一脸懵逼。如下图:
这里写图片描述

该类中都是些空实现的静态内部类,没有任何与界面加载相关的内容。这是为什么呢?看上面有句英文注释就明白了,意思是这些子类是为了启动特定独立的 Settings 选项而创建的,例如在某个应用里需要设置无线那么只需要启动无线对应的类就可以了,而没必要打开settings应用再点击wifi设置项进行设置。再看此类继承于 SettinggsActivity,这时我们就应该可以想到,初始化界面应该在它父类 SettinggsActivity 里完成的。为了方便讲解,我们先以wifi设置页面WifiSettingsActivity 的直接跳转为例,详细讲解这个启动流程。懂了这个之后,其他子页面的启动自然就明白了。

接下来我们在清单文件中找到 WifiSettingsActivity 的定义如下:
  这里写图片描述
  
其中有 meta-data 的标签使用,从这个标签的 key-value 来看,很明显可以认为WifiSettings的具体实现应该是由 WifiSettings 这个 Fragment 来布局渲染的。然后我们回到 SettingsActivity 中,找到 onCreate() 方法如下:
这里写图片描述

可以看到,一进入 oncreate 里有个 getMetaData(), 这和我们之前看到的清单文件里的meta似乎有某种联系,点进去看,代码如下:
这里写图片描述

可以看到,这个函数的主要作用就是从 Activity 标签中获取 meta-data 标签中key为 com.android.settings.FRAGMENT_CLASS 的值,并将其赋值给 mFragmentClass 这个私有变量。
以 WifiSettingsActivity为 例,从这个 Activity 中 meta-data 标签中获取的信息为 com.android.settings.wifi.WifiSettings,即mFragmentClass=”com.android.settings.wifi.WifiSettings”。
getMetaData() 执行完后紧接着执行了 getIntent(),getMetaData() 上面有句注释 should happen before any call to getIntent。意思是 getIntent() 必须在 getMetaData() 之后执行,其实这也有原因的,点进 getIntent() 方法看看就知道了。代码具体如下:
这里写图片描述

继续看 getStartingFragmentClass():
这里写图片描述

从源码看以看出,getIntent 的作用就是构造了一个 Intent,并且给它增加了一个特殊的键值对,key为”:settings:show_fragment”,value为 mFragmentClass 指定的 Fragment 类名。
之所以要先执行getMetaData,是因为 mFragmentClass 赋值是在 getMeatData 中进行的。

明白之后我们继续分析onCreate()方法:

    final ComponentName cn = intent.getComponent();
    final String className = cn.getClassName();// 本例中,className为WifiSettingsActivity
    mIsShowingDashboard = className.equals(Settings.class.getName()); //因此这里为false
        ...
        ...
            setContentView(mIsShowingDashboard ?
           R.layout.settings_main_dashboard : R.layout.settings_main_prefs);//本例中这里选择了后者
          ...
          ...
            } else {
                if (!mIsShowingDashboard) {//因为mIsShowingDashboard为false,所以会到这里
                    ....
    //initialArguments通过赋值保存了meta-data中指定的com.android.settings.wifi.WifiSettings
      Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
                   switchToFragment(initialFragmentName, initialArguments, true, false,
                            mInitialTitleResId, mInitialTitle, false);//走到这里进行fragment替换
               } else {
                    // No UP affordance if we are displaying the main Dashboard
                    mDisplayHomeAsUpEnabled = false;
                    // Show Search affordance
                    mDisplaySearch = true;
                    mInitialTitleResId = R.string.dashboard_title;
                   switchToFragment(DashboardSummary.class.getName(), null, false, false,
                            mInitialTitleResId, mInitialTitle, false);
                }
            }

我们来具体看一下 switchToFragment() 方法:
这里写图片描述

通过 FragmentTransaction 的 replace 方法,将Fragment的布局在 R.id.main_content 指定的位置进行渲染。

2.主界面启动流程

上面讲的是没有通过点击 Settings 主界面的选项而直接打开子界面的启动过程,下面我们介绍通过点击setting主界面的选项进入子界面的过程。
  通过前面的讲解我们知道,mIsShowingDashboard 的值( true/false )是确实加载主界面还是子界面的唯一条件。我们回到相关代码:

    final ComponentName cn = intent.getComponent();
    final String className = cn.getClassName();// 因为从主界面启动,所以这里className为Settings
    mIsShowingDashboard = className.equals(Settings.class.getName()); //因此这里变成了true
        ...
        ...
            setContentView(mIsShowingDashboard ?
           R.layout.settings_main_dashboard : R.layout.settings_main_prefs);//选择了前者
          ...
          ...
          } else {
                if (!mIsShowingDashboard) {//因为mIsShowingDashboard为true,不走这里了
                    ....

                   Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
                   switchToFragment(initialFragmentName, initialArguments, true, false,
                            mInitialTitleResId, mInitialTitle, false);//
               } else {//从主界面进入,走这里
                    // No UP affordance if we are displaying the main Dashboard
                    mDisplayHomeAsUpEnabled = false;
                    // Show Search affordance
                    mDisplaySearch = true;
                    mInitialTitleResId = R.string.dashboard_title;
                   switchToFragment(DashboardSummary.class.getName(), null, false, false, mInitialTitleResId, mInitialTitle, false);//接下来重点分析这里
                }
            }

从上面分析可以知道如果从主界面进入的话 switchToFragment 会将当前页面替换成 DashboardSummary,我们看一下 DashboardSummary.java 的代码:
这里写图片描述

这是一个 fragment,在 onCreateView 里,填充了 dashboard.xml. 来看一下这个布局:
这里写图片描述

这是一个垂直可滚动的线性结构,很容易联想到我们手机里的设置主页面,的确如此。再继续看DashboardSummary 代码,在 onResume() 里:
这里写图片描述

有 SendReBuildUI(),点进去查看:
这里写图片描述

原来里面是在发消息,找到消息的接收者:
这里写图片描述

终于发现了里面的 reBuildUI 的方法:

private void rebuildUI(Context context) {
      if (!isAdded()) {
          Log.w(LOG_TAG, "Cannot build the DashboardSummary UI yet as the Fragment is not added");
          return;
      }

      long start = System.currentTimeMillis();
      final Resources res = getResources();

      mDashboard.removeAllViews();
    //(1)这里调用SettingActivity的getDashboardCategories,也就是加载整个Setting的内容
      List<DashboardCategory> categories =
              ((SettingsActivity) context).getDashboardCategories(true);//注意该方法

      final int count = categories.size();

      for (int n = 0; n < count; n++) {
          DashboardCategory category = categories.get(n);

          View categoryView = mLayoutInflater.inflate(R.layout.dashboard_category, mDashboard,
                  false);

          TextView categoryLabel = (TextView) categoryView.findViewById(R.id.category_title);
          categoryLabel.setText(category.getTitle(res));

          ViewGroup categoryContent =
                  (ViewGroup) categoryView.findViewById(R.id.category_content);

          final int tilesCount = category.getTilesCount();
          for (int i = 0; i < tilesCount; i++) {
              DashboardTile tile = category.getTile(i);
//(2)创建DashboardTileView,也就是每个Setting的内容
              DashboardTileView tileView = new DashboardTileView(context);
              updateTileView(context, res, tile, tileView.getImageView(),
                      tileView.getTitleTextView(), tileView.getStatusTextView());

              tileView.setTile(tile);

              categoryContent.addView(tileView);
          }

          // Add the category
          mDashboard.addView(categoryView);
      }
      long delta = System.currentTimeMillis() - start;
      Log.d(LOG_TAG, "rebuildUI took: " + delta + " ms");
  }

接下来对上面两处注释进行说明:
(1)处 rebuildUI 里调用 getDashboardCategories() 方法,该方法如下:

这里写图片描述

这个方法里又调用了 buildDashboardCategories() 方法:
这里写图片描述

看到这里终于明白了,里面有个对 dashboard_categories.xml 的处理, loadCategoriesFromResource() 方法就不看了,它的作用是解析 dashboard_categories.xml 这个 xml 文件。我们看一下dashboard_categories.xml 吧:
这里写图片描述

部分截图,没有截图,因为内容太多了。不过从这局部就可以看出这对应的就是我们设置主页面的各个选项。
这里写图片描述

(2)处将通过 for 循环遍历而来的数据通过创建 DashboardTileView 最终全部存入到 mDashboard 这个布局中,至此整个 Setting 模块的界面布局已经完成了。

在 DashboardTileView.java 里,有个 onclick 方法,这就是 settings 主页面每个子选项的点击事件了,通过点击进入不同的子设置选项,如 wifi,蓝牙等。
这里写图片描述

至此,Settings 的启动方式讲解完了,下面附一张自己手画的一张 Settings 启动流程的草图,画的比较丑,凑合的看….:
这里写图片描述

参考文章:
http://www.itdadao.com/articles/c15a227784p0.html
http://blog.csdn.net/wzy_1988/article/details/50556113

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 系统 Settings 启动流程详解 的相关文章

随机推荐

  • GO中的 & 和 * 在创建结构体对象场景中的含义

    在赋值场景中 amp 和 分别是取地址和取值的两个特殊操作 xff0c 是最基础的用法和语义 当 出现在参数中或者声明场景时 xff0c 代表传参类型需要指针类型或者声明一个指针变量 基本类型的应用场景 xff1a a 61 1 声明变量a
  • VMware 桥接模式下设置centos8.5的静态IP

    centos8 5安装在vmware虚拟机中 xff0c 为了将虚拟机中的centos8 5的IP和宿主机的IP地址设置在同一网段 xff0c 需要我们首先指定虚拟机的网络模式为桥接模式 xff0c 并将虚拟机桥接至你的能够联网的真实网卡上
  • Returned error: invalid argument 0: json: cannot unmarshal non-string into Go value of type common.H

    区块链使用web3 js与geth以太坊节点进行通讯时 xff0c 有时候节点会响应Returned error invalid argument 0 json cannot unmarshal non string into Go val
  • web3.js 提交事务 Error: Returned error: invalid sender

    初学使用web3 js提交事务到以太坊时 xff0c 会遇到 xff1a Error Returned error invalid sender 以太坊返回提示 xff1a 发送者无效 产生此问题的根本原因是事务提交操作的链信息没有指定 x
  • hadoop2.7.2 window win7 基础环境搭建

    hadoop环境搭建相对麻烦 xff0c 需要安装虚拟机过着cygwin什么的 xff0c 所以通过查资料和摸索 xff0c 在window上搭建了一个 xff0c 不需要虚拟机和cygwin依赖 xff0c 相对简便很多 下面运行步骤除了
  • Spring4.3.3 WebSocket-STOMP协议集成 (2.1)-WebSocket-stomp子协议通讯小栗子

    上一篇中说到 xff1a stomp jsp之间的通讯 xff0c 是通过stomp xff0c socket js stomp js实现的通讯 如果觉得写jsp或者js麻烦 xff0c 或者不怎么会js 不是太懂 xff0c 可以用web
  • Spring-Boot (二) application.properties配置文件内容

    Spring Boot官方开发指导文档 默认创建spring boot项目后 xff0c 会在resources目录下生成一个空的application properties配置文件 xff0c springboot启动时加载该配置文件 a
  • Nginx 基本配置例子

    个人的小笔记 xff0c 不好不好 user root worker processes 8 error log var log nginx error log error pid var run nginx pid load module
  • CKPlayer播放M3U8文件 (加载失败)问题

    官网下载 ckplayer csdn下载 csdn 下载本地以后 解压ckplayer zip 里面有示例文件 index html flashplayer html等 index html中基本包含所有功能 flashplayer htm
  • 超市购物小票案例

    超市购物小票案例 1 1超市购物小票需求分析 模拟真实购物逻辑 xff0c 以上述图片为需求原型 xff0c 根据不同购买物品 xff0c 完成购物小票内容打印到控制台 简化项目逻辑 xff0c 票脚部分只完成数据统计 数据 xff1a 将
  • 【4】三剑客:sed

    文章目录 一 基础1 1 语法格式1 2 工作原理 二 功能2 1 增2 2 删2 3 改2 4 查2 5 通用2 6 高级用法 一 基础 1 1 语法格式 span class token function sed span span c
  • libgtk2.0-dev安装

    安装libgtk 2 0 dev报错 xff1a sudo apt get install libgtk2 0 dev 正在读取软件包列表 完成 正在分析软件包的依赖关系树 正在读取状态信息 完成 有一些软件包无法被安装 如果您用的是 un
  • 第3章 vmware虚拟机的三种网络设置

    在vmware虚拟机的设置中 xff0c 共有四种网络模式可以选择 xff1a 桥接模式 NAT模式 仅主机模式和自定义模式 本文重点讲解前三种网络模式的设置 下图是我的宿主机的IP信息 xff0c 在某些场景下需要将虚拟机中的centos
  • 无法安装64位版本的office,因为在您的PC上找到以下32位程序

    无法安装64位版本的office 因为在您的PC上找到以下32位程序 请卸载所有32位office程序 xff0c 然后重试安装64位office 如果想要安装32位office xff0c 请运行32位安装程序 那为什么会出现这种情况呢
  • idea调试rt.jar下sun.misc.*的源码

    1 进入设置页面 xff1a Settings gt Debugger gt Stepping 2 在 34 Do not step into these classes 34 列表中 xff0c 去掉 34 sun 34 前面的勾就可以了
  • iOS UILabel的lineBreakMode省略模式设置

    iOS UILabel的lineBreakMode省略模式设置
  • 将网页分享到微信JS

    1 导入微信的js lt script type 61 34 text javascript 34 src 61 34 http res wx qq com open js jweixin 1 0 0 js 34 gt lt script
  • 利用XML文件的一个写日志的类!!!!!

    对于程序执行期间的错误跟踪 xff01 相信大家都有自己的一套办法 xff01 xff01 xff01 但都是利用文件文件 xff0c 我这次利用的是XML amp XSL xff0c 可产生报表格式的日志 轻松生成报表 xff01 xff
  • 解决golang获取时间默认使用UTC

    在Go语言上 xff0c go语言的time Now 返回的是当地时区时间 xff0c 直接用 xff1a time Now Format 2006 01 02 15 04 05 输出的是当地时区时间 但是部署之后 xff0c 有的服务器会
  • Android 系统 Settings 启动流程详解

    Settings简介 Settings 是 Android 系统自带的一个很重要的应用 xff0c 给用户提供了操作 Android 系统功能的界面 它里面包含了 Wireless amp network xff0c device xff0