20150623----Android-Settings源码分析
来源:互联网 发布:99宿舍软件 编辑:程序博客网 时间:2024/06/07 03:18
</pre><p>Settings的时序图:</p><p><img src="http://img.blog.csdn.net/20150623143540579?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2hhZGFpb2Rld2FuZ3dlaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" height="451" width="679" alt="" /></p><p></p><p align="center"><span style="font-weight:bold">1.本文说明</span></p> <p> 本文主要针对L平台上Settings模块正常启动流程做一个简要分析,并试着分析一下Settings下面Storage选项的实现过程。</p> <p> </p> <p> </p> <p align="center"><strong>2.Settings概览</strong></p> <p> 在之前的KK平台上Settings模块的第一个Activity名字为Settings,其继承的是PreferenceActivity,设置的每一个选项都是对应的一个Header对象,并且Header对象允许显示switch控件,button控件,checkbox控件等。如下图2.1,WLAN和蓝牙上使用到了switch开关。但在L上面,WLAN和蓝牙的这两个开关已经去掉了,如图2.2,在Settings模块的首个页面似乎就只是一个普通的Listview,那它用的还是不是Header呢?或者说取而代之的是什么呢?下一节详细说明。<img src="http://img.blog.csdn.net/20150623144026713?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2hhZGFpb2Rld2FuZ3dlaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /> KK Settings首届面 <img src="http://img.blog.csdn.net/20150623144058131?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2hhZGFpb2Rld2FuZ3dlaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" height="300" width="177" alt="" />L Settings首届面</p><p></p><p></p><p align="center"><strong> 3 .L Settings 模块首界面初始化流程</strong></p> <p> </p> <p>L Settings模块首界面为Settings,继承自SettingsActivity,SettingsActivity继承自Activity。</p> <p> </p> <p>首先看一下Settings.java代码可以发现它没有重写任何SettingsActiviy的方法,也没有增加任何自己的方法,唯独增加了许多静态内部类,如:</p><p><pre name="code" class="java"> /* * Settings subclasses for launching independently. */ public static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ } public static class WirelessSettingsActivity extends SettingsActivity { /* empty */ } public static class SimSettingsActivity extends SettingsActivity { /* empty */ } public static class TetherSettingsActivity extends SettingsActivity { /* empty */ } public static class VpnSettingsActivity extends SettingsActivity { /* empty */ } public static class DateTimeSettingsActivity extends SettingsActivity { /* empty */ } public static class StorageSettingsActivity extends SettingsActivity { /* empty */ } public static class WifiSettingsActivity extends SettingsActivity { /* empty */ }
看注释可以知道,这些子类是为了启动特定独立的Settings选项而创建的,例如在某个应用里需要设置无线那么只需要启动 WirelessSettingsActivity 就可以了。
所以Settings模块的启动流程直接看SettingsActiviy就行了。
3.1 SettingsActivity.onCreate方法
onCreate方法是Activity的生命周期第一步,看看 SettingsActivity在这里都做了些什么?
// Should happen before any call to getIntent() getMetaData();
这个方法用来获得Activity的额外数据mFragmentClass,如果可以获得这个数据,那么下面会去显示mFragmentClass对应的Activity。直接启动Settings模块不会获得这个数据。
mIsShowingDashboard = className.equals(Settings.class.getName());
这一步很重要,因为我们是从Settings这个Activity过来的,所以这里的 mIsShowingDashboard 为 true 。
// 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);这个判断很重要但很明显这时isSubSettings的值是fasle,暂时忽略。 setContentView(mIsShowingDashboard ? R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
由于mIsShowingDashboard为true,直接走到下面这段
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这个Fragment.
接下来就看看DashboardSummary是个什么玩意?
dashboard中文意思是仪表盘,这里是指DashboardSummary就是用来显示Settings所有选项的。
在DashboardSummary的onCreateView里加载了这个布局文件R.layout.dashboard:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/dashboard" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbarStyle="outsideOverlay" android:clipToPadding="false"> <LinearLayout android:id="@+id/dashboard_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center_horizontal" android:paddingStart="@dimen/dashboard_padding_start" android:paddingEnd="@dimen/dashboard_padding_end" android:paddingTop="@dimen/dashboard_padding_top" android:paddingBottom="@dimen/dashboard_padding_bottom" android:orientation="vertical" /> </ScrollView>
看了上面的布局文件可以知道Settings的选项视图应该就是显示在dashboard_container中了。
DashboardSummary走完onCreateView方法后会走onResume,然后一路下来又会调到SettingsActivity的
loadCategoriesFromResource(R.xml.dashboard_categories, categories);
过程试这样的:DashboardSummary.java中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(); List<DashboardCategory> categories = ((SettingsActivity) context).getDashboardCategories(true);......}SettingsActivity.java中的getDashboardCategories(boolean b)
public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) { if (forceRefresh || mCategories.size() == 0) { buildDashboardCategories(mCategories); } return mCategories; }
buildDashboardCategories()方法:
/** * Called when the activity needs its list of categories/tiles built. * * @param categories The list in which to place the tiles categories. */ private void buildDashboardCategories(List<DashboardCategory> categories) { categories.clear(); loadCategoriesFromResource(R.xml.dashboard_categories, categories); updateTilesList(categories); }
loadCategoriesFromResource()方法:
/** * Parse the given XML file as a categories description, adding each * parsed categories and tiles into the target list. * * @param resid The XML resource to load and parse. * @param target The list in which the parsed categories and tiles should be placed. */ private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) { XmlResourceParser parser = null; try { parser = getResources().getXml(resid); AttributeSet attrs = Xml.asAttributeSet(parser); int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { // Parse next until start tag is found } String nodeName = parser.getName(); if (!"dashboard-categories".equals(nodeName)) { throw new RuntimeException( "XML document must start with <preference-categories> tag; found" + nodeName + " at " + parser.getPositionDescription()); } Bundle curBundle = null; final int outerDepth = parser.getDepth(); while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } nodeName = parser.getName(); if ("dashboard-category".equals(nodeName)) { DashboardCategory category = new DashboardCategory(); TypedArray sa = obtainStyledAttributes( attrs, com.android.internal.R.styleable.PreferenceHeader); category.id = sa.getResourceId( com.android.internal.R.styleable.PreferenceHeader_id, (int)DashboardCategory.CAT_ID_UNDEFINED); TypedValue tv = sa.peekValue( com.android.internal.R.styleable.PreferenceHeader_title); if (tv != null && tv.type == TypedValue.TYPE_STRING) { if (tv.resourceId != 0) { category.titleRes = tv.resourceId; } else { category.title = tv.string; } } sa.recycle(); final int innerDepth = parser.getDepth(); while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String innerNodeName = parser.getName(); if (innerNodeName.equals("dashboard-tile")) { DashboardTile tile = new DashboardTile(); sa = obtainStyledAttributes( attrs, com.android.internal.R.styleable.PreferenceHeader); tile.id = sa.getResourceId( com.android.internal.R.styleable.PreferenceHeader_id, (int)TILE_ID_UNDEFINED); tv = sa.peekValue( com.android.internal.R.styleable.PreferenceHeader_title); if (tv != null && tv.type == TypedValue.TYPE_STRING) { if (tv.resourceId != 0) { tile.titleRes = tv.resourceId; } else { tile.title = tv.string; } } tv = sa.peekValue( com.android.internal.R.styleable.PreferenceHeader_summary); if (tv != null && tv.type == TypedValue.TYPE_STRING) { if (tv.resourceId != 0) { tile.summaryRes = tv.resourceId; } else { tile.summary = tv.string; } } tile.iconRes = sa.getResourceId( com.android.internal.R.styleable.PreferenceHeader_icon, 0); tile.fragment = sa.getString( com.android.internal.R.styleable.PreferenceHeader_fragment); sa.recycle(); if (curBundle == null) { curBundle = new Bundle(); } final int innerDepth2 = parser.getDepth(); while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String innerNodeName2 = parser.getName(); if (innerNodeName2.equals("extra")) { getResources().parseBundleExtra("extra", attrs, curBundle); XmlUtils.skipCurrentTag(parser); } else if (innerNodeName2.equals("intent")) { tile.intent = Intent.parseIntent(getResources(), parser, attrs); } else { XmlUtils.skipCurrentTag(parser); } } if (curBundle.size() > 0) { tile.fragmentArguments = curBundle; curBundle = null; } // Show the SIM Cards setting if there are more than 2 SIMs installed. if(tile.id != R.id.sim_settings || Utils.showSimCardTile(this)){ category.addTile(tile); } } else { XmlUtils.skipCurrentTag(parser); } } target.add(category); } else { XmlUtils.skipCurrentTag(parser); } } } catch (XmlPullParserException e) { throw new RuntimeException("Error parsing categories", e); } catch (IOException e) { throw new RuntimeException("Error parsing categories", e); } finally { if (parser != null) parser.close(); } }
这一步是通过 R.xml.dashboard_categories来加载categories,这里的categorys为ArrayList<DashboardCategory>mCategories。接着来看看dashboard_categories.xml这个文件吧
<dashboard-categories xmlns:android="http://schemas.android.com/apk/res/android"> <!-- ----------------------------------------WIRELESS and NETWORKS ----------------------------------------> <dashboard-category android:id="@+id/wireless_section" android:title="@string/header_category_wireless_networks" > <!-- Wifi --> <dashboard-tile android:id="@+id/wifi_settings" android:title="@string/wifi_settings_title" android:fragment="com.android.settings.wifi.WifiSettings" android:icon="@drawable/ic_settings_wireless" /> <!-- Bluetooth --> <!-- Hotknot --> <!-- SIM Cards --> <!-- Data Usage --><!-- Call Settings --> <dashboard-tile android:id="@+id/call_settings" android:title="@string/call_settings" android:icon="@drawable/ic_menu_phone"> <intent android:action="android.intent.action.MAIN" android:targetPackage="com.android.dialer" android:targetClass="com.android.dialer.settings.DialerSettingsActivity" /> </dashboard-tile> <!-- Operator hook --> <dashboard-tile android:id="@+id/operator_settings" android:fragment="com.android.settings.WirelessSettings" > <intent android:action="com.android.settings.OPERATOR_APPLICATION_SETTING" /> </dashboard-tile> <!-- Other wireless and network controls --> <dashboard-tile android:id="@+id/wireless_settings" android:title="@string/radio_controls_title" android:fragment="com.android.settings.WirelessSettings" android:icon="@drawable/ic_settings_more" /> </dashboard-category> <!--------------------------------------------------------------------------- DEVICE --------------------------------------------------> <dashboard-category android:id="@+id/device_section" android:title="@string/header_category_device" > <!-- Home --> <dashboard-tile android:id="@+id/home_settings" android:title="@string/home_settings" android:fragment="com.android.settings.HomeSettings" android:icon="@drawable/ic_settings_home" /> 。。。<pre name="code" class="html"> </dashboard-category>。。。。。。</dashboard-categories>
根据这个文件我们可以知道了,所谓的dashboard就是Settings模块首界面的一个抽象。而dashboard-categorys则是设置分类集合的抽象,而dashboard-category是分类的抽象,dashboard-tile就是分类下每个选项的抽象了。代码中的List<DashboardCategory>对应dashboard-categorys, DashboardCategory对应dashboard-category,而dashboard-tile则对因代码中的DashboardTile。
当加载完这些对象后SettingsActivity会将得到的 mCategories 返回给DashboardSummary来初始化Settings的设置选项。
下面这段代码就是DashboardSummary.rebuildUI()中完成界面的初始化
long start = System.currentTimeMillis(); final Resources res = getResources(); mDashboard.removeAllViews(); 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); 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); }
这段代码我就不具体分析了,逻辑很简单,遍历categories这个列表来获取DashboardCategory对象,将所有DashboardCategory对象和DashboardCategory对象中的DashboardTile对象转化为视图对象并添加到主视图对象mDashboard中。
到这里SettingsActivity的onCreate方法就算结束了。总结一下,
1.onCreate完成的任务是切换DashboardSmmary这个Fragment,然后从dashboard_categories.xml中读取预先配置好的文件来初始化Settings的首界面视图。
2.L中舍弃了Header类,取而代之的是DashboardCategory和DashboardTile类。
- 20150623----Android-Settings源码分析
- Android Settings源码流程分析
- Android原生Settings源码分析
- Android Settings(系统设置)源码分析(一)
- Android 5.1 Settings源码简要分析
- Android 5.1 Settings源码简要分析
- Android 5.1settings源码简要分析
- Android Settings(系统设置)源码分析(一)
- Android 5.1 Settings模块源码分析
- Android 5.1 Settings源码简要分析
- Settings源码分析
- Scrapy-settings源码分析
- Settings源码分析
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(二)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(二)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- [HDU1789]Doing Homework again[贪心]
- 使用easyBCD 安装 Win8 + Ubuntu 13.10 双系统
- java-计算任意日期所在周、月、年的第一天与最后一天
- 2015.06.23
- Eclipse自动生成作者、日期注释等功能
- 20150623----Android-Settings源码分析
- Load resources from relative path using local html in uiwebview
- 17. 解决冲突
- Android.mk添加第三方jar包
- java学习之旅09--char_字符串入门_boolean
- 哪些企业会在云计算中活下来
- Linux进程间通信(IPC)
- CSS Bootstrap简介
- ios 企业发布ipa 和 plist