Android 5.1 Settings模块源码分析

来源:互联网 发布:unity3d 模拟重力跳跃 编辑:程序博客网 时间:2024/05/16 06:13

前述:

本人已工作两年多,但是依然感觉还是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

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

同时继续往下走,会看到这段代码块:

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,代码如下:

<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>


接下来将是重点,开始真正加载Setting的界面了,在OnResume方法中最终会调用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这个View就是整个界面的总View        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)处最终会调用SettingActivity的buildDashboardCategories方法,


private void buildDashboardCategories(List<DashboardCategory> categories) {        categories.clear();        loadCategoriesFromResource(R.xml.dashboard_categories, categories);        updateTilesList(categories);}

该方法将加载一个xml文档并使用Android默认的xml解析器XmlPullParser对文档进行解析,最终将解析结果存入到一个List<DashboardCategory>中,然后在上面代码的rebuildUI方法中for循环遍历读取。


以下为Setting页面的xml文档:


<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2014 The Android Open Source Project     Licensed under the Apache License, Version 2.0 (the "License");     you may not use this file except in compliance with the License.     You may obtain a copy of the License at          http://www.apache.org/licenses/LICENSE-2.0     Unless required by applicable law or agreed to in writing, software     distributed under the License is distributed on an "AS IS" BASIS,     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.     See the License for the specific language governing permissions and     limitations under the License.--><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 -->        <dashboard-tile                android:id="@+id/bluetooth_settings"                android:title="@string/bluetooth_settings_title"                android:fragment="com.android.settings.bluetooth.BluetoothSettings"                android:icon="@drawable/ic_settings_bluetooth2"                />        <!-- SIM Cards -->        <dashboard-tile                android:id="@+id/sim_settings"                android:title="@string/sim_settings_title"                android:fragment="com.android.settings.sim.SimSettings"                android:icon="@drawable/ic_sim_sd"                />        <!-- Data Usage -->        <dashboard-tile                android:id="@+id/data_usage_settings"                android:title="@string/data_usage_summary_title"                android:fragment="com.android.settings.DataUsageSummary"                android:icon="@drawable/ic_settings_data_usage"                />        <!-- 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" >        <!-- Hard key -->        <dashboard-tile                android:id="@+id/hard_key_settings"                android:title="@string/hard_key_settings"                android:icon="@drawable/ic_settings_hardkey">            <intent               android:action="android.intent.action.MAIN"               android:targetClass="com.android.settings.HardKeySettings"               android:targetPackage="com.android.settings" />         </dashboard-tile>        <!-- 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"                />        <!-- Display -->        <dashboard-tile                android:id="@+id/display_settings"                android:title="@string/display_settings"                android:fragment="com.android.settings.DisplaySettings"                android:icon="@drawable/ic_settings_display"                />                <!-- SPRD:add AudioProfile in setting @{ -->        <dashboard-tile               android:id="@+id/notification_settings"               android:icon="@drawable/ic_settings_notifications"               android:title="@string/audio_profiles" >               <intent               android:action="android.intent.action.MAIN"               android:targetClass="com.sprd.audioprofile.AudioProfileSettings"               android:targetPackage="com.sprd.audioprofile" />        </dashboard-tile>        <!-- @} -->        <!-- SPRD:add notification in setting @{ -->        <!-- Notifications -->        <dashboard-tile                android:id="@+id/notification_settings"                android:title="@string/prompt_notification_settings"                android:fragment="com.android.settings.notification.NotificationSettings"                android:icon="@drawable/ic_settings_situation"                />        <!-- Storage -->        <dashboard-tile                android:id="@+id/storage_settings"                android:title="@string/storage_settings"                android:fragment="com.android.settings.deviceinfo.Memory"                android:icon="@drawable/ic_settings_storage"                />        <!-- Battery -->        <dashboard-tile                android:id="@+id/battery_settings"                android:title="@string/power_usage_summary_title"                android:fragment="com.android.settings.fuelgauge.PowerUsageSummary"                android:icon="@drawable/ic_settings_battery"                />        <!-- Application Settings -->        <dashboard-tile                android:id="@+id/application_settings"                android:title="@string/applications_settings"                android:fragment="com.android.settings.applications.ManageApplications"                android:icon="@drawable/ic_settings_applications"                />        <!-- Uninstall application -->        <dashboard-tile                android:id="@+id/uninstall_settings"                android:title="@string/delete_applications"                android:fragment="com.android.settings.applications.SprdUninstallApplications"                android:icon="@drawable/ic_settings_uninstall"                />        <!-- Manage users -->        <dashboard-tile                android:id="@+id/user_settings"                android:title="@string/user_settings_title"                android:fragment="com.android.settings.users.UserSettings"                android:icon="@drawable/ic_settings_multiuser"                />        <!-- Manage NFC payment apps -->        <dashboard-tile                android:id="@+id/nfc_payment_settings"                android:title="@string/nfc_payment_settings_title"                android:fragment="com.android.settings.nfc.PaymentSettings"                android:icon="@drawable/ic_settings_nfc_payment"                />        <!-- Manufacturer hook -->        <dashboard-tile                android:id="@+id/manufacturer_settings"                android:fragment="com.android.settings.WirelessSettings">            <intent android:action="com.android.settings.MANUFACTURER_APPLICATION_SETTING" />        </dashboard-tile>    </dashboard-category>    <!-- PERSONAL -->    <dashboard-category            android:id="@+id/personal_section"            android:title="@string/header_category_personal" >        <!-- Location -->        <dashboard-tile                android:id="@+id/location_settings"                android:title="@string/location_settings_title"                android:fragment="com.android.settings.location.LocationSettings"                android:icon="@drawable/ic_settings_location"                />        <!-- Security -->        <dashboard-tile                android:id="@+id/security_settings"                android:title="@string/security_settings_title"                android:fragment="com.android.settings.SecuritySettings"                android:icon="@drawable/ic_settings_security"                />        <!-- Account -->        <dashboard-tile                android:id="@+id/account_settings"                android:title="@string/account_settings_title"                android:fragment="com.android.settings.accounts.AccountSettings"                android:icon="@drawable/ic_settings_accounts"                />        <!-- Language -->        <dashboard-tile                android:id="@+id/language_settings"                android:title="@string/language_settings"                android:fragment="com.android.settings.inputmethod.InputMethodAndLanguageSettings"                android:icon="@drawable/ic_settings_language"                />        <!-- Backup and reset -->        <dashboard-tile                android:id="@+id/privacy_settings"                android:title="@string/privacy_settings"                android:fragment="com.android.settings.PrivacySettings"                android:icon="@drawable/ic_settings_backup"                />        <!-- SPRD: add by Bug 372436, Regular boot development @{ -->        <dashboard-tile            android:id="@+id/power_alarm"            android:icon="@drawable/ic_settings_power_off"            android:title="@string/swtichmachine" >            <intent                android:action="android.intent.action.MAIN"                android:targetClass="com.sprd.settings.timerpower.AlarmClock"                android:targetPackage="com.android.settings" />        </dashboard-tile>    <!-- @} -->    </dashboard-category>    <!-- SYSTEM -->    <dashboard-category        android:id="@+id/system_section"        android:title="@string/header_category_system" >        <!-- Date & Time -->        <dashboard-tile                android:id="@+id/date_time_settings"                android:title="@string/date_and_time_settings_title"                android:fragment="com.android.settings.DateTimeSettings"                android:icon="@drawable/ic_settings_date_time"                />        <!-- Accessibility feedback -->        <dashboard-tile                android:id="@+id/accessibility_settings"                android:title="@string/accessibility_settings"                android:fragment="com.android.settings.accessibility.AccessibilitySettings"                android:icon="@drawable/ic_settings_accessibility"                />        <!-- Print -->        <dashboard-tile                android:id="@+id/print_settings"                android:title="@string/print_settings"                android:fragment="com.android.settings.print.PrintSettingsFragment"                android:icon="@drawable/ic_settings_print"                />        <!-- Development -->        <dashboard-tile                android:id="@+id/development_settings"                android:title="@string/development_settings_title"                android:fragment="com.android.settings.DevelopmentSettings"                android:icon="@drawable/ic_settings_development"                />        <!-- About Device -->        <dashboard-tile                android:id="@+id/about_settings"                android:title="@string/about_settings"                android:fragment="com.android.settings.DeviceInfoSettings"                android:icon="@drawable/ic_settings_about"                />    </dashboard-category></dashboard-categories>

根据这个文件可看出来,dashboard-categories这个标签对应着Java代码中的List<DashboardCategory>集合,dashboard-category这个标签对应着DashboardCategory类,dashboard-tile这个标签对应着DashboardTile这个类。


(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数据结构。

1 0