Android Settings模块架构浅析<1>

概述

Android Settings模块说简单也简单,说难也难,里面涉及到的知识点也挺多的。

我们知道Settings主要是用于配置一些系统选项或属性值,通过修改设置项就能达到修改系统配置的作用。

那么问题来了,Settings是如何实现修改后能改变系统配置的呢?Settings又是采用怎样的架构实现的呢?里面又涉及到哪些知识点呢?

让我们一起来揭开她的神秘面纱吧!

原理分析

Settings的主要功能就是改变系统配置,那么他是如何做到的呢?
  • Settings的战友SettingsProvider
通过跟踪Settings源码发现,Settings并不是孤军作战,它还有一个战友 - SettingsProvider。

SettingsProvider又是做什么的呢?其实看到这个名字我们不难猜到他扮演着什么样的角色。

SettingsProvider继承ContentProvider,ContentProvider在android中主要扮演着数据共享的角色。

SettingsProvider中有一个数据库,并且这个数据库是对外公开的。

Settings与SettingsProvider之间又是什么关系呢?且看下面分析。
  • Settings和SettingsProvider之间的关系
Settings源码位置:
  packages/apps/Settings/

SettingsProvider源码位置:
  frameworks/base/packages/SettingsProvider/
  frameworks/base/core/java/android/provider/Settings.java

db在数据库中存在的位置:
  /data/data/com.android.providers.settings/databases/settings.db
他们之间存在什么联系呢?

其实,Settings会对SettingsProvider中的数据库进行操作和监听。

Settings中大部分选项都会涉及到对SettingsProvider的操作。
  • 原理分析
通过跟踪代码发现,Settings大部分操作的就是SettingsProvider中的数据,也有一些直接操作系统属性的等等。

当用户在修改系统设置时,大部分实际上是在修改SettingsProvider中的值。

当SettingsProvider数据库中的值被改变时,一些系统服务什么的就会监听到,这时候就会通过jni等当时操作底层,从而达到系统属性或配置改变的效果。

架构分析

Settings处在安卓的应用层,不同于市场上的app,Settings属于系统app,也是一个比较特别的app。
  • Settings特点
1.Settings页面很多,但是Activity却很少,基本上都是使用PreferenceFragment

2.Settings中包含大量对provider的操作与监听

3.Settings UI基本上都是采用Preference来实现
  • Settings架构
1.Settings主界面Activity使用的是Settings

2.Settings子界面Activity基本上都是使用SubSettings

3.Settings与SubSettings中都是空Activity,这里的空Activity指的是没有重写7大生命周期方法

4.Settings与SubSettings都是继承于SettingsActivity

5.主界面使用的layout是:settings_main_dashboard,子界面使用的layout是:settings_main_prefs

6.主界面settings_main_dashboard中是使用DashboardSummary(Fragment)进行填充,子界面都是使用各自的Fragment进行填充

7.子界面fragment基本上都是直接或间接继承SettingsPreferenceFragment

8.主界面选项列表是定义在dashboard_categories.xml中,此文件是在SettingsActivity的buildDashboardCategories方法中进行解析的

9.在Settings类中定义了很多static class,这些类都是继承SettingsActivity,但都是空的,如BluetoothSettingsActivity
  这些类主要用于对外提供跳转页面,比如从SystemUI跳转至Settings中的某个界面

10.Settings类中定义了的static class被定义在AndroidManifest中,通过meta-data参数将对应的Fragment绑定在一起

11.在Activity中填充Fragment主要使用的是SettingsActivity中的switchToFragment方法
  • settings_main_dashboard中只有一个FrameLayout,后面会将其替换为DashboardSummary
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/dashboard_background_color" />
  • settings_main_prefs中也存在一个叫main_content的FrameLayout,后面会将其替换为各自的Fragment,switch_bar与button_bar只有在某些页面才会显示
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0px"
        android:layout_weight="1"
        android:orientation="vertical" >

        <com.android.settings.widget.SwitchBar
            android:id="@+id/switch_bar"
            android:layout_width="match_parent"
            android:layout_height="?android:attr/actionBarSize"
            android:background="@drawable/switchbar_background"
            android:theme="?attr/switchBarTheme" />

        <FrameLayout
            android:id="@+id/main_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="?attr/preferenceBackgroundColor" />
    </LinearLayout>

    <RelativeLayout
        android:id="@+id/button_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="0"
        android:visibility="gone" >

        <Button
            android:id="@+id/back_button"
            android:layout_width="150dip"
            android:layout_height="wrap_content"
            android:layout_alignParentStart="true"
            android:layout_margin="5dip"
            android:text="@*android:string/back_button_label" />

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:orientation="horizontal" >

            <Button
                android:id="@+id/skip_button"
                android:layout_width="150dip"
                android:layout_height="wrap_content"
                android:layout_margin="5dip"
                android:text="@*android:string/skip_button_label"
                android:visibility="gone" />

            <Button
                android:id="@+id/next_button"
                android:layout_width="150dip"
                android:layout_height="wrap_content"
                android:layout_margin="5dip"
                android:text="@*android:string/next_button_label" />
        </LinearLayout>
    </RelativeLayout>

</LinearLayout>
  • Settings主界面结构

1.从图中可以看到,红色框中的属于一个DashboardCategory,蓝色框中的属于DashboardTileView
2.在DashboardSummary中有多个DashboardCategory,DashboardCategory中包含一个title和多个DashboardTileView
3.DashboardTileView具有onClick方法,点击后启动子界面,使用的是Utils.startWithFragment进行跳转
4.startWithFragment方法中将子界面的Fragment传递给activity,这里会绑定对应的activity,也就是SubSettings

Utils.startWithFragment关键方法

 public static void startWithFragment(Context context, String fragmentName, Bundle args,
            Fragment resultTo, int resultRequestCode, int titleResId,
            CharSequence title) {
        startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
                null /* titleResPackageName */, titleResId, title, false /* not a shortcut */);
    }

    public static void startWithFragment(Context context, String fragmentName, Bundle args,
            Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
            CharSequence title, boolean isShortcut) {
        Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
                titleResPackageName,
                titleResId, title, isShortcut);
        if (resultTo == null) {
            context.startActivity(intent);
        } else {
            resultTo.startActivityForResult(intent, resultRequestCode);
        }
    }

    public static Intent onBuildStartFragmentIntent(Context context, String fragmentName,
            Bundle args, String titleResPackageName, int titleResId, CharSequence title,
            boolean isShortcut) {
        Intent intent = new Intent(Intent.ACTION_MAIN);
        if (BluetoothSettings.class.getName().equals(fragmentName)) {
            intent.setClass(context, SubSettings.BluetoothSubSettings.class);
            intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, true);
        } else if (WifiSettings.class.getName().equals(fragmentName)) {
            intent.setClass(context, SubSettings.WifiSubSettings.class);
            intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, true);
        } else {
            intent.setClass(context, SubSettings.class);
        }
        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, fragmentName);
        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,
                titleResPackageName);
        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId);
        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut);
        return intent;
    }

SettingsActivity.onCreate方法中的关键代码

    @Override
    protected void onCreate(Bundle savedState) {
        super.onCreate(savedState);

        // Should happen before any call to getIntent()
        getMetaData();

        final Intent intent = getIntent();

        // Getting Intent properties can only be done after the super.onCreate(...)
        final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);

        mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
                intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);

        final ComponentName cn = intent.getComponent();
        final String className = cn.getClassName();

        mIsShowingDashboard = className.equals(Settings.class.getName());

        // This is a "Sub Settings" when:
        // - this is a real SubSettings
        // - or :settings:show_fragment_as_subsetting is passed to the Intent
        final boolean isSubSettings = className.equals(SubSettings.class.getName()) ||
                intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);

        setContentView(mIsShowingDashboard ?
                R.layout.settings_main_dashboard : R.layout.settings_main_prefs);

        mContent = (ViewGroup) findViewById(R.id.main_content);

        getFragmentManager().addOnBackStackChangedListener(this);

        if (savedState != null) {
            ......
        } else {
            if (!mIsShowingDashboard) {
                ......
                setTitleFromIntent(intent);

                Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
                switchToFragment(initialFragmentName, initialArguments, true, false,
                        mInitialTitleResId, mInitialTitle, false);
            } else {
                ......
                mInitialTitleResId = R.string.dashboard_title;
                switchToFragment(DashboardSummary.class.getName(), null, false, false,
                        mInitialTitleResId, mInitialTitle, false);
            }
        }
      ......
    }
1.当点击主界面上的item时会调用Utils.startWithFragment方法
2.在Utils.startWithFragment会跳转至SubSettings,对应的fragment也作为参数传递给了SubSettings
3.SubSettings是一个空的activity,但SubSettings继承于SettingsActivity,因此会调用父类SettingsActivity的onCreate方法
4.在onCreate方法中,className为SubSettings,isSubSettings为true,mIsShowingDashboard为false
5.因此会执行switchToFragment(initialFragmentName, initialArguments, true, false, mInitialTitleResId, mInitialTitle, false);
6.通过switchToFragment将settings_main_prefs的main_content替换为了子界面对应的fragment

简单类图

从下面类图中可以看出

1.Settings中主要的Activity为SettingsActivity,其他基本上都是继承该activity,并且其他基本上都是空的
2.Settings中fragment基本上都是继承至SettingsPreferenceFragment


时序图

下面的时序图为点击Settings图标启动Settings,在点击item启动子界面的时序图
从图中可以看出启动的一个流程,按照这个流程,几乎所有的界面都会执行SettingsActivity
时间: 2024-10-05 22:32:13

Android Settings模块架构浅析<1>的相关文章

Android应用内社区SDK技术架构浅析

Android应用内社区SDK技术架构浅析 应用内微社区是什么 ? 图1 图2 雪球财经应用内社区 一.以友盟微社区为例 简单来说,友盟的微社区就是一款帮助开发者在应用中快速搭建一个社区( 类似于新浪微博.朋友圈 )的产品.在很多应用中,开发商往往都会需要一个用户之间以及用户与开发商之间互动的社区,用户往往又会在社区里选择赞.评论.关注.转发.发帖等各种互动方式.但是开发一个社交系统可不是那么容易的一件事,复杂的用户关系.消息流.服务器架构等等都是棘手的问题,更重要的是大家都是重复地劳动!每个开

Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程

关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM  版本:基于android4.2之前版本 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)欢迎指正错误,共同学习.共同进步!!一.Android Bluetooth Architecture蓝牙代码架构部分(google 官方蓝牙框架) Android的蓝牙系统,自下而上包括以下一些

Android WiFi--系统架构

http://blog.csdn.net/myarrow/article/details/8129607 1. 系统架构 Android WiFi系统引入了wpa_supplicant,它的整个WiFi系统以wpa_supplicant为核心来定义上层用户接口和下层驱动接口.整个WiFi系统架构如下图所示: 一切尽在上图中,下面将对每部分进行详细分析. 1.1 WifiService 由SystemServer启动的时候生成的ConnecttivityService创建,负责启动关闭wpa_su

Camera服务之--架构浅析

Camera服务之--架构浅析 分类: Camera 分析2011-12-22 11:17 7685人阅读 评论(3) 收藏 举报 android硬件驱动框架jnilinux内核平台 一.应用层 Camera 的应用层在Android 上表现为直接调用SDK API 开发的一个Camera 应用APK 包.代码在/android/packages/apps/Camera 下.主要对 android.hardware.Camera(在Framework中) 类的调用,并且实现Camera 应用的业

Android bluetooth介绍(两): android 蓝牙源架构和uart 至rfcomm过程

关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM  版本号:基于android4.2先前版本 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载,请注明作者.请尊重版权谢谢)欢迎指正错误,共同学习.共同进步!! Android bluetooth介绍(一):基本概念及硬件接口Android bluetooth介绍(二): android 蓝牙代码架构及其uart

Android HAL模块实现(转)

Android的HAL(Hardware Abstract Layer硬件抽象层)是为了保护一些硬件提供商的知识产权而提出的,是为了避开linux的GPL束缚.思路是把控制硬件的动作都放到了Android HAL中,而linux driver仅仅完成一些简单的数据交互作用,甚至把硬件寄存器空间直接映射到user space.而Android是基于Aparch的license,因此硬件厂商可以只提供二进制代码,所以说Android只是一个开放的平台,并不是 一个开源的平台. 总结下来,Androi

Android HAL模块实现

1. HAL介绍 Android的HAL(Hardware Abstract Layer硬件抽象层)是为了保护一些硬件提供商的知识产权而提出的.是为了避开linux的GPL束缚. 思路是把控制硬件的动作都放到了Android HAL中,而linux driver仅仅完毕一些简单的数据交互作用,甚至把硬件寄存器空间直接映射到user space.而Android是基于Aparch的license,因此硬件厂商能够仅仅提供二进制代码,所以说Android仅仅是一个开放的平台,并非一个开源的平台. 总

React Native Android 源码框架浅析(主流程及 Java 与 JS 双边通信)

[工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果.私信联系我] 1 背景 有了前面<React Native Android 从学车到补胎和成功发车经历>和<React Native Android Gradle 编译流程浅析>两篇文章的学习我们 React Native 已经能够基本接入处理一些事情了,那接下来的事情就是渐渐理解 RN 框架的一些东西,以便裁剪和对 RN 有个更深入的认识,所以本篇总结了我这段时间阅读源码

LAMP架构浅析

  LAMP架构浅析 一.LAMP简介 Linux+Apache+Mysql/MariaDB+Perl/PHP/Python一组常用来搭建动态网站或者服务器的开源软件,本身都是各自独立的程序,但是因为常被放在一起使用,拥有了越来越高的兼容度,共同组成了一个强大的Web应用程序平台.LAMP具有Web资源丰富.轻量.快速开发等特点,微软的.NET架构相比,LAMP具有通用.跨平台.高性能.低价格的 优势,因此LAMP无论是性能.质量还是价格都是企业搭建网站的首选平台. 二.本实验目的 通过简单编译