浅谈Android Settings模块架构

浅谈Android Settings模块架构

概述

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

 

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

 

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

 

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

原理分析

Settings的主要功能就是改变系统配置,那么他是如何做到的呢?

Settings的战友SettingsProvider

通过跟踪Settings源码发现,Settings并不是孤军作战,它还有一个战友 - SettingsProvider。

 

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

 

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

 

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

 

Settings与SettingsProvider之间又是什么关系呢?且看下面分析。

 

SettingsSettingsProvider之间的关系

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类中定义了很多staticclass,这些类都是继承SettingsActivity,但都是空的,如BluetoothSettingsActivity

  这些类主要用于对外提供跳转页面,比如从SystemUI跳转至Settings中的某个界面

 

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

 

11.在Activity中填充Fragment主要使用的是SettingsActivity中的switchToFragment方法

 

settings_main_dashboard中只有一个FrameLayout,后面会将其替换为DashboardSummary

<framelayoutandroid:background="@color/dashboard_background_color"android:id="@+id/main_content"android:layout_height="match_parent"android:layout_width="match_parent"xmlns:android="http://schemas.android.com/apk/res/android"/>

settings_main_prefs中也存在一个叫main_contentFrameLayout,后面会将其替换为各自的Fragmentswitch_barbutton_bar只有在某些页面才会显示

 

<linearlayoutandroid:layout_height="match_parent"android:layout_width="match_parent"android:orientation="vertical"xmlns:android="http://schemas.android.com/apk/res/android"><linearlayout android:layout_height="0px"android:layout_weight="1" android:layout_width="match_parent"android:orientation="vertical"><com.android.settings.widget.switchbar android:background="@drawable/switchbar_background"android:id="@+id/switch_bar"android:layout_height="?android:attr/actionBarSize"android:layout_width="match_parent"android:theme="?attr/switchBarTheme"><framelayout android:background="?attr/preferenceBackgroundColor"android:id="@+id/main_content"android:layout_height="match_parent"android:layout_width="match_parent"/></com.android.settings.widget.switchbar></linearlayout><relativelayout android:id="@+id/button_bar"android:layout_height="wrap_content"android:layout_weight="0" android:layout_width="match_parent"android:visibility="gone"><buttonandroid:id="@+id/back_button"android:layout_alignparentstart="true"android:layout_height="wrap_content"android:layout_margin="5dip"android:layout_width="150dip"android:text="@*android:string/back_button_label"type="submit"><linearlayoutandroid:layout_alignparentend="true"android:layout_height="wrap_content"android:layout_width="wrap_content"android:orientation="horizontal"></linearlayout></button><buttonandroid:id="@+id/skip_button"android:layout_height="wrap_content"android:layout_margin="5dip"android:layout_width="150dip"android:text="@*android:string/skip_button_label"android:visibility="gone" type="submit"></button><buttonandroid:id="@+id/next_button"android:layout_height="wrap_content"android:layout_margin="5dip"android:layout_width="150dip"android:text="@*android:string/next_button_label"type="submit"></button></relativelayout></linearlayout><buttonandroid:id="@+id/skip_button"android:layout_height="wrap_content"android:layout_margin="5dip"android:layout_width="150dip"android:text="@*android:string/skip_button_label"android:visibility="gone" type="submit"></button>

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,

                            Bundleargs, Fragment resultTo, int resultRequestCode,

                            inttitleResId, CharSequence title) {

                   startWithFragment(context,fragmentName, args, resultTo,

                                     resultRequestCode,null /* titleResPackageName */, titleResId,

                                     title,false /* not a shortcut */);

         }

 

         public staticvoid startWithFragment(Context context, String fragmentName,

                            Bundleargs, Fragment resultTo, int resultRequestCode,

                            StringtitleResPackageName, int titleResId, CharSequence title,

                            booleanisShortcut) {

                   Intentintent = 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,

                            StringfragmentName, Bundle args, String titleResPackageName,

                            inttitleResId, CharSequence title, boolean isShortcut) {

                   Intentintent = 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);

                   returnintent;

         }

SettingsActivity.onCreate方法中的关键代码

@Override protectedvoid onCreate(BundlesavedState) { super.onCreate(savedState); // Should happen before any call togetIntent() getMetaData(); final Intent intent = getIntent(); // Getting Intentproperties can only be done after the super.onCreate(...) final StringinitialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT); mIsShortcut =isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false); finalComponentName 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 finalboolean 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启动子界面的时序图

?

1

从图中可以看出启动的一个流程,按照这个流程,几乎所有的界面都会执行

SettingsActivity

 

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值