以setting—>system—>Languages & input—>Languages 这个点击路径来说,图1依托于Settings图2依托于.Settings$xxxActivity,图三和图4是在hierarchy view中显示为SubSettings。(别的不一定,例如Display中并没有依托于SubSettings)。
图1图2
图3
图4 image.png
那么SubSettings和Settings以及Settings$xxxActivity的差别是什么?
//packages\apps\Settings\src\com\android\settings\SubSettings.java
/**
* Stub class for showing sub-settings; we can't use the main Settings class
* since for our app it is a special singleTask class.
*/
public class SubSettings extends SettingsActivity {
@Override
public boolean onNavigateUp() {
finish();
return true;
}
@Override
protected boolean isValidFragment(String fragmentName) {
Log.d("SubSettings", "Launching fragment " + fragmentName);
return true;
}
}
/**
* Top-level Settings activity
*/
public class Settings extends SettingsActivity {
.........
public static class LanguageAndInputSettingsActivity extends SettingsActivity { /* empty */ }
.........
public static class SystemDashboardActivity extends SettingsActivity {}
}
从定义上来看,虽然Settings 和SubSettings 都是继承自SettingsActivity ,但是Settings中只是定义了一堆不重写函数的Settings$xxxActivity,SubSettings 中简单定义了几个函数,目前看不出差异。
//AndroidManifest.xml
......
<activity android:name=".Settings$SystemDashboardActivity"
android:label="@string/header_category_system"
android:icon="@drawable/ic_settings_about">
<intent-filter android:priority="-1">
<action android:name="com.android.settings.action.SETTINGS"/>
</intent-filter>
<meta-data android:name="com.android.settings.category"
android:value="com.android.settings.category.ia.homepage"/>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.system.SystemDashboardFragment"/>
<meta-data android:name="com.android.settings.summary"
android:resource="@string/system_dashboard_summary"/>
</activity>
......
<activity android:name=".Settings$LanguageAndInputSettingsActivity"
android:label="@string/language_settings"
android:icon="@drawable/ic_settings_language"
android:taskAffinity="com.android.settings"
android:parentActivityName="Settings$SystemDashboardActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.VOICE_LAUNCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter android:priority="260">
<action android:name="com.android.settings.action.SETTINGS"/>
</intent-filter>
<meta-data android:name="com.android.settings.category"
android:value="com.android.settings.category.ia.system"/>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.language.LanguageAndInputSettings"/>
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true"/>
</activity>
......
图三显示的是.Settings$LanguageAndInputSettingsActivity
,LanguageAndInputSettingsActivity是由LanguageAndInputSettings这个fragment具体定义显示的内容,而LanguageAndInputSettings extends DashboardFragment,与此同时,上一级的SystemDashboardFragment也是继承自DashboardFragment,并且SystemDashboardActivity 的写法也是与LanguageAndInputSettingsActivity类似, 都是定义在Settings类中的,并且fragment的显示也都是通过SettingsActivity中的launchSettingFragment函数去显示。那么又是怎么跟SubSettings 扯上关系?为什么LanguageAndInputSettingsActivity在hierarchy view中显示的是SubSettings,而SystemDashboardActivity 是Settings$SystemDashboardActivity
。
由于SettingsActivity中判断条件是
final boolean isSubSettings = this instanceof SubSettings || intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
通过打印log
Log.d(TAG,"this instanceof SubSettings = " + String.valueOf(this instanceof SubSettings) + " , getBooleanExtra " + String.valueOf(intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false)));
发现
01-02 01:43:22.792 1155-1155/? D/SettingsActivity: this instanceof SubSettings = true , getBooleanExtra false
判断是否为SubSettings关键在this instanceof SubSettings。那么为什么LanguageAndInputSettingsActivity在hierarchy view中显示是SubSettings?
在点击system下的Languages & input 的时候,会调用SettingsActivity中的startPreferencePanel函数。
//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\settings\SettingsActivity.java
/**
* Start a new fragment containing a preference panel. If the preferences
* are being displayed in multi-pane mode, the given fragment class will
* be instantiated and placed in the appropriate pane. If running in
* single-pane mode, a new activity will be launched in which to show the
* fragment.
*
* @param fragmentClass Full name of the class implementing the fragment.
* @param args Any desired arguments to supply to the fragment.
* @param titleRes Optional resource identifier of the title of this
* fragment.
* @param titleText Optional text of the title of this fragment.
* @param resultTo Optional fragment that result data should be sent to.
* If non-null, resultTo.onActivityResult() will be called when this
* preference panel is done. The launched panel must use
* {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
* @param resultRequestCode If resultTo is non-null, this is the caller's
* request code to be received with the result.
*/
public void startPreferencePanel(Fragment caller, String fragmentClass, Bundle args,
int titleRes, CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Log.d(TAG,"startPreferencePanel");
String title = null;
if (titleRes < 0) {
if (titleText != null) {
title = titleText.toString();
} else {
// There not much we can do in that case
title = "";
}
}
Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
titleRes, title, mIsShortcut, mMetricsFeatureProvider.getMetricsCategory(caller));
}
而startWithFragment中又调用了onBuildStartFragmentIntent,这里构造了一个intent, intent.setClass(context, SubSettings.class),ComponentName的class为SubSettings,因此hierarchy view中显示SubSettings,而没有显示真正的activity的名字。至于这样做的原因,由于本人知识的局限性,目前没有想到。
//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\settings\Utils.java
......
public static void startWithFragment(Context context, String fragmentName, Bundle args,
Fragment resultTo, int resultRequestCode, int titleResId,
CharSequence title, boolean isShortcut, int metricsCategory) {
Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
null /* titleResPackageName */, titleResId, title, isShortcut, metricsCategory);
if (resultTo == null) {
context.startActivity(intent);
} else {
resultTo.getActivity().startActivityForResult(intent, resultRequestCode);
}
}
........
public static Intent onBuildStartFragmentIntent(Context context, String fragmentName,
Bundle args, String titleResPackageName, int titleResId, CharSequence title,
boolean isShortcut, int sourceMetricsCategory) {
Log.d(TAG,"onBuildStartFragmentIntent");
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClass(context, SubSettings.class);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, fragmentName);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,
titleResPackageName);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut);
intent.putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY, sourceMetricsCategory);
return intent;
}
.............
到了这里,基本上捋清楚了显示流程。
总结就是:通过SettingsActivity中的switchToFragment,去显示所有的界面,其他的这些定义的activity只是一个用来定义fragment的壳子。
另外,使用中可能需要通过intent跳转到setting中的界面。如
startActivity(new Intent(Settings.ACTION_SETTINGS));
根据官方文档
https://developer.android.com/reference/android/provider/Settings.html#ACTION_APP_NOTIFICATION_SETTINGS可知
Settings.ACTION_SETTINGS ,Constant Value: "android.settings.SETTINGS"即下面AndroidManifest.xml中的action 标签中的所定义的name。
<activity android:name="Settings"
android:taskAffinity="com.android.settings"
android:label="@string/settings_label_launcher"
android:launchMode="singleTask">
<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>
.........................
<activity android:name="Settings$AppNotificationSettingsActivity"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="android.settings.APP_NOTIFICATION_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.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.notification.AppNotificationSettings" />
</activity>
因此,如果新增的activity,只要定义新的action然后使用这个string去新建intent,最后通过startActivity(intent)跳转。
网友评论