Android Settings 应用加载流程(一)

Android Settings 应用加载流程(一)

AndroidMainfest.xml 文件中找到入口文件,分析启动流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<application
android:allowBackup="false"
android:hardwareAccelerated="true"
android:requiredForAllUsers="true"
android:supportsRtl="true"
android:taskAffinity=""
android:theme="@style/Theme.Settings"
android:usesCleartextTraffic="true" >
<!-- Settings -->
<activity
android:name="Settings"
android:label="@string/settings_label_launcher"
android:launchMode="singleTask"
android:taskAffinity="com.android.settings" >
<intent-filter android:priority="1" >
<action android:name="android.settings.SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true" />
</activity>
<!-- Alias for launcher activity only, as this belongs to each profile. -->
<activity-alias
android:name="Settings"
android:label="@string/settings_label_launcher"
android:launchMode="singleTask"
android:targetActivity="Settings"
android:taskAffinity="com.android.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>
// ...
</application>

Settings应用的入口为 Settings.java 要分析 Settings 应用,就需要从 Settings.java 入手。

Settings.java

1
2
3
4
5
6
7
8
9
public class Settings extends SettingsActivity {
/*
* 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 */ }
// ...
}

Settings.java 中并没有具体的实现代码,而是定义了很多内部类,这些内部类和 Settings 一样都继承自 SettingsActivity。
Settings.java 中定义的内部类在 manifest.xml 文件中,也可以看到它的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<activity
android:name="Settings$BluetoothSettingsActivity"
android:label="@string/bluetooth_settings_title"
android:taskAffinity="" >
<intent-filter android:priority="1" >
<action android:name="android.settings.BLUETOOTH_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.VOICE_LAUNCH" />
<category android:name="com.android.settings.SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.bluetooth.BluetoothSettings" />
<meta-data
android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
android:resource="@id/bluetooth_settings" />
</activity>

Settings.java 中内部类的定义甚至没有一行代码实现,其实它的实现逻辑就是根据在 manifest.xml 中配置在 activity 节点中的 meta-data 为 com.android.settings.FRAGMENT_CLASS 的值去加载对应的 fragment 。
SettingsActivity 只是一个模板类,具体的UI以及业务流程都是在 Fragment 去完成了;

SettingsActivity.java

onCreate

1
2
3
4
5
6
7
8
9
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
// Should happen before any call to getIntent()
getMetaData();
// ...
final Intent inteng = getIntent();
// ...
}

getMetaData

1
2
3
4
5
6
7
8
9
10
11
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());
}
}

在该方法中去获取配置在 manifest.xml 中的 META_DATA_KEY_FRAGMENT_CLASS (“com.android.settings.FRAGMENT_CLASS”) 。

getIntent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Override
public Intent getIntent() {
Intent superIntent = super.getIntent();
String startingFragment = getStartingFragmentClass(superIntent);
// This is called from super.onCreate, isMultiPane() is not yet reliable
if (startingFragment != null) { // Fragment 名称不为空
Intent modIntent = new Intent(superIntent);
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment); // 设置 Fragment 类名
Bundle args = superIntent.getExtras();
if (args != null) {
args = new Bundle(args);
} else {
args = new Bundle();
}
args.putParcelable("intent", superIntent);
modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args); // 保存参数
return modIntent;
}
return superIntent;
}
private String getStartingFragmentClass(Intent intent) {
if (mFragmentClass != null) return mFragmentClass;
// 如果 mFragmentClass 不为空,立即返回,也就是上面所说的在 manifest.xml 中配置了 META_DATA_KEY_FRAGMENT_CLASS 值的Activity
String intentClass = intent.getComponent().getClassName();
if (intentClass.equals(getClass().getName())) return null; // Settings Activity
if ("com.android.settings.ManageApplications".equals(intentClass)
|| "com.android.settings.RunningServices".equals(intentClass)
|| "com.android.settings.applications.StorageUse".equals(intentClass)) {
// Old names of manage apps.
intentClass = com.android.settings.applications.ManageApplications.class.getName();
}
return intentClass;
}

getIntent() 默认的行为被重写了,通过 getStartingFragmentClass() 方法获取需要加载的 Fragment 名称,如果是 Settings 入口页面,则返回 null;
如果 getStartingFragmentClass 中获取到的 FragmentClass 不为空,则重新创建一个新的 Intent 对象返回,并且额外添加了 EXTRA_SHOW_FRAGMENT_ARGUMENTSEXTRA_SHOW_FRAGMENT 参数;