Android(安卓)5.1 Settings模块源码分析
前述:
本人已工作两年多,但是依然感觉还是Android的门外汉,之前一直从事Android的应用开发,每天就是各种调用SDK方法,各种拷贝网上的源码以及jar包,从来也不管为啥这样用,由于换了一份工作才开始接触到Android的源码,感觉Android的水好深啊。
今天这篇博客也是我的处女作啊,以后也希望通过多多研究源码来写出更多的博客,我觉得写博客主要还是作为一个记录吧,不然感觉有的东西真的很容易丢,尤其是平时不怎么接触的模块。
好啦,接下来开始今天的Setting旅行啦。
Settings简述:
Setting模块大家还是比较熟悉的吧?其实Setting也不是什么高级的东西,它就是一个APP,属于Android的应用层,源码在packages\apps\Settings中,今天分析的源码是基于Android5.1,如下图是5.1Setting模块的界面:
查看一个应用首先是查看这个应用的AndroidManifest.xml文件,以便查看程序的入口,Setting模块的入口是Setting.java这个类,这个类继承SettingActivity,但是没有继承任何的方法,但却定义了一大堆内部类。
/** * Top-level Settings activity */public class Settings extends SettingsActivity { public static class DateTimeSettingsActivity extends SettingsActivity { /* empty */ } public static class StorageSettingsActivity extends SettingsActivity { /* empty */ } public static class WifiSettingsActivity extends SettingsActivity { /* empty */ } public static class WifiP2pSettingsActivity extends SettingsActivity { /* empty */ } public static class InputMethodAndLanguageSettingsActivity extends SettingsActivity { /* empty */ } public static class KeyboardLayoutPickerActivity extends SettingsActivity { /* empty */ } public static class InputMethodAndSubtypeEnablerActivity extends SettingsActivity { /* empty */ } public static class VoiceInputSettingsActivity extends SettingsActivity { /* empty */ } public static class SpellCheckersSettingsActivity extends SettingsActivity { /* empty */ } public static class LocalePickerActivity extends SettingsActivity { /* empty */ } public static class UserDictionarySettingsActivity extends SettingsActivity { /* empty */ } public static class HomeSettingsActivity extends SettingsActivity { /* empty */ } public static class DisplaySettingsActivity extends SettingsActivity { /* empty */ } public static class DeviceInfoSettingsActivity extends SettingsActivity { /* empty */ } public static class ApplicationSettingsActivity extends SettingsActivity { /* empty */ } public static class ManageApplicationsActivity extends SettingsActivity { /* empty */ }}
这些类都是Setting模块的子界面类,是特定功能的类,比如WifiSettingsActivity是WiFi模块相关的类。
所以接下来我们直接分析SettingActivity这个类就可以了。
SettingActivity.java:
先看该类的OnCreate方法
@Override protected void onCreate(Bundle savedState) { super.onCreate(savedState); // Should happen before any call to getIntent() getMetaData(); final Intent intent = getIntent();
先调用getMetaData()方法,用于加载一些元数据,进入getMetaData()方法
private void getMetaData() { try { ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); if (ai == null || ai.metaData == null) return; mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); } catch (NameNotFoundException nnfe) { // No recovery Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString()); }}
主要作用就是通过META_DATA_KEY_FRAGMENT_CLASS这个属性获得额外的mFragmentClass,如果可以获得将启动对应的mFragmentClass的Activity,但是直接启动Setting不会获得该数据。
继续往下看代码
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 Intentfinal boolean isSubSettings = className.equals(SubSettings.class.getName()) || intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
由于我们是从Setting启动的,所以mIsShowingDashboard的值为true,而isSubSettings的值是false。
setContentView(mIsShowingDashboard ?R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
由于mIsShowingDashboard的值为true,所以使用的是R.layout.settings_main_dashboard
同时继续往下走,会看到这段代码块:
if (savedState != null) { ... } else { if (!mIsShowingDashboard) { ... } else {// No UP affordance if we are displaying the main DashboardmDisplayHomeAsUpEnabled = false; // Show Search affordance mDisplaySearch = true; mInitialTitleResId = R.string.dashboard_title; switchToFragment(DashboardSummary.class.getName(), null, false, false, mInitialTitleResId, mInitialTitle, false); }}
这里由于是第一次启动,所以savedState 为null,同时mIsShowingDashboard的值为true,看到进入了switchToFragment这个方法,这里准备切换到DashboardSummary这个Fragment。
DashboardSummary.java
DashboardSummary的onCreateView方法加载了R.layout.dashboard,代码如下:
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这个View就是整个界面的总View mDashboard.removeAllViews();(1)这里调用SettingActivity的getDashboardCategories,也就是加载整个Setting的内容 List 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)处最终会调用SettingActivity的buildDashboardCategories方法,
private void buildDashboardCategories(List categories) { categories.clear(); loadCategoriesFromResource(R.xml.dashboard_categories, categories); updateTilesList(categories);}
该方法将加载一个xml文档并使用Android默认的xml解析器XmlPullParser对文档进行解析,最终将解析结果存入到一个List
以下为Setting页面的xml文档:
<?xml version="1.0" encoding="utf-8"?>
根据这个文件可看出来,dashboard-categories这个标签对应着Java代码中的List
(2)处将通过for循环遍历而来的数据通过创建DashboardTileView最终全部存入到mDashboard这个布局中,至此整个Setting模块的界面布局已经完成了。
DashboardTileView.java
这个类是Setting中每个条目数据的类,通过onClick方法启动不同的功能,比如WiFi,Bluetooth等
public class DashboardTileView extends FrameLayout implements View.OnClickListener { @Override public void onClick(View v) { if (mTile.fragment != null) { Utils.startWithFragment(getContext(), mTile.fragment, mTile.fragmentArguments, null, 0, mTile.titleRes, mTile.getTitle(getResources())); } else if (mTile.intent != null) { getContext().startActivity(mTile.intent); } }}
最终启动不同的Setting子模块, 至此整个Setting模块的整体框架就已分析结束了。
总结一下:
1、整个Setting模块是在SettingActivity中加载DashboardSummary这个Fragment,然后从dashboard_categories.xml中读取预先配置好的文件来初始化Settings的首界面视图。
2、Setting的子界面基本上都是一个Fragment,并且基本上都是通过SettingActivity的OnCreate方法去加载的,同时大部分SubSetting在加载界面时,用的都是PreferenceFragment技术(在以后的博客中会讲述)。
3、Android5.1的Setting使用的是DashboardCategory和DashboardTile类来存储整个xml数据结构。
更多相关文章
- android google 分屏 多窗口 按home键界面错乱故障分析(一)分屏的
- 客觀評 Android、iOS、WP7
- 移动语音引擎相关开发笔记
- 改变Android按钮背景颜色的高效方法
- 作为Android开发者 你真的知道Android按下开机键到启动发生什么
- Android核心分析28篇,强烈推荐android初学者,android进阶者看看这
- Android(安卓)横向ScrollView照片浏览器实现
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用