您的位置:首页 > 编程语言

androoid framework学习之Settings的主界面的代码流程分析

2016-12-28 14:40 330 查看
今天开始学setting的相关代码流程,主要是主界面生成的流程。

1.Settings简述:

Setting模块大家很熟悉把,那你知道它是怎么形成的吗?其实Setting也不是什么高级的东西,它就是一个APP,属于Android的应用层,源码在packages\apps\Settings中,今天分析的源码是基于Android5.1,那下面还是先来看下她长得怎么样,是不是感觉自己也懂呀,哈哈,



2.那要知道一个模块的入口,就必须要看这个应用的AndroidManifest.xml文件。那问题来了该怎么从文件中找入口呢?

其实很简单只需找到该文件中</intent-filter>内的匹配条件是
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

然后找到这个intent所在的activity,找到对应的android:name,这里是Setting,所以setting模块的入口就是setting.java.

<!-- Alias for launcher activity only, as this belongs to each profile. -->
<activity-alias android:name="Settings"
android:taskAffinity="com.android.settings"
android:label="@string/settings_label_launcher"
android:launchMode="singleTask"
android:targetActivity="Settings">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>


3.那下面是不是该来看下setting类都是些什么鸟?

这个类继承SettingActivity,但是没有继承任何的方法,但却定义了一大堆内部类。

[java] view
plain copy

/**

* 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模块相关的类。

4.所以接下来我们直接分析SettingActivity这个类就可以了。

SettingActivity.java:

那这个类首先执行的就是OnCreate函数,他都做了些什么呢?直接看源码吧

[java] view
plain copy

@Override

protected void onCreate(Bundle savedState) {

super.onCreate(savedState);

// Should happen before any call to getIntent()

getMetaData();

final Intent intent = getIntent();

先调用getMetaData()方法,用于加载一些元数据,进入getMetaData()方法

[java] view
plain copy

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不会获得该数据。

继续往下看代码

[java] view
plain copy

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




由于我们是从Setting启动的,所以mIsShowingDashboard的值为true,而isSubSettings的值是false。

[java] view
plain copy

setContentView(mIsShowingDashboard ?

R.layout.settings_main_dashboard : R.layout.settings_main_prefs);



由于mIsShowingDashboard的值为true,所以使用的是R.layout.settings_main_dashboard

[html] view
plain copy

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

/>

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

[java] view
plain copy

if (savedState != null) {

...

} else {

if (!mIsShowingDashboard) {

...

} 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);

}

}



这里由于是第一次启动,所以savedState 为null,同时mIsShowingDashboard的值为true,看到进入了switchToFragment这个方法,这里准备切换到DashboardSummary这个Fragment。

DashboardSummary.java

DashboardSummary的onCreateView方法,

中会调用加载了R.layout.dashboard,代码如下:

[html] view
plain copy

<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的界面了,注意到在DashboardSummary的onResume方法中,有个方法为sendRebuildUI,这个方法通过Handler发送Message来通知界面更新UI,更新UI的方法为rebuildUI。,该方法源码:

@Override
public void onResume() {
super.onResume();

mLte4GEnabler.resume();

  sendRebuildUI();

final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addDataScheme("package");
getActivity().registerReceiver(mHomePackageReceiver, filter);

// Register for intent broadcasts
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
getActivity().registerReceiver(mReceiver, intentFilter);
}


private static final int MSG_REBUILD_UI = 1;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REBUILD_UI: {
final Context context = getActivity();
     rebuildUI(context);
} break;
}
}
};


在rebuildUI里面是这样执行的:

[java] view
plain copy

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");

}

public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) {
if (forceRefresh || mCategories.size() == 0) {
buildDashboardCategories(mCategories);
}
return mCategories;
}


接下来将对上面代码标注的序号处进行说明:

(1)处最终会调用SettingActivity的buildDashboardCategories方法,

[java] view
plain copy

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文档:

[html] view
plain copy

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

&nbs
1aa73
p; 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等

[java] view
plain copy

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数据结构
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: