Android P Settings
Settings启动流程分析
1.直接跳转子界面
设置是整个android系统的比较重要应用,所涉及到的都是系统功能。
设置为了更方便的区分和获取信息,在AndroidManifest.xml文件中添加了许多的属性,这可能导致很多人看着头疼,但是这也是设置的精髓所在.
第一步:分析AndroidManifest文件,可以看到Settings.java是应用的入口类。
<!-- Alias for launcher activity only, as this belongs to each profile. -->
<activity-alias android:name="Settings"
android:taskAffinity="com.android.settings.root"
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>
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
</activity-alias>
从代码中可以清楚的看到,Settings 的启动类为 Settings。从 Settings 源码中我们找到了Settings.java文件。。看下面代码:
public class Settings extends SettingsActivity {
/*
* Settings subclasses for launching independently.
*/
public static class AssistGestureSettingsActivity extends SettingsActivity { /* empty */}
public static class BluetoothSettingsActivity 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 PrivateVolumeForgetActivity extends SettingsActivity { /* empty */ }
public static class PrivateVolumeSettingsActivity extends SettingsActivity { /* empty */ }
public static class PublicVolumeSettingsActivity extends SettingsActivity { /* empty */ }
public static class WifiSettingsActivity extends SettingsActivity { /* empty */ }
public static class WifiP2pSettingsActivity extends SettingsActivity { /* empty */ }
public static class AvailableVirtualKeyboardActivity extends SettingsActivity { /* empty */ }
public static class KeyboardLayoutPickerActivity extends SettingsActivity { /* empty */ }
Settings中都是些空实现的静态内部类,没有任何与界面加载相关的内容。意义在于这些子类是为了启动特定独立的 Settings 选项而创建的。
我们看SettingsActivity,它继承于SettingsDrawerActivity,而SettingsDrawerActivity内容不多,主要功能是:
1、注册对应用安装、卸载、更新的事件广播,同时更新一级设置列表。
@Override
protected void onResume() {
super.onResume();
//添加应用安装,卸载,改变,更新监听事件
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");
registerReceiver(mPackageReceiver, filter);
new CategoriesUpdateTask().execute();
}
2、重写setContentView,让子类调用的setContentView时,将子类布局填充到自己content_frame中。
@Override
public void setContentView(@LayoutRes int layoutResID) {
final ViewGroup parent = findViewById(R.id.content_frame);
if (parent != null) {
parent.removeAllViews();
}
LayoutInflater.from(this).inflate(layoutResID, parent);
}
3、当接受到广播时,调用CategoriesUpdateTask,异步更新sTileBlacklist列表,并回调监听事件。
private class CategoriesUpdateTask extends AsyncTask<Void, Void, Void> {
private final CategoryManager mCategoryManager;
public CategoriesUpdateTask() {
mCategoryManager = CategoryManager.get(SettingsDrawerActivity.this);
}
@Override
protected Void doInBackground(Void... params) {
mCategoryManager.reloadAllCategories(SettingsDrawerActivity.this, getSettingPkg());
return null;
}
@Override
protected void onPostExecute(Void result) {
mCategoryManager.updateCategoryFromBlacklist(sTileBlacklist);
onCategoriesChanged();
}
}
我们接着分析Settings中的静态内部类,通过一个例子来了解这个启动流程,如果有别的应用想进入WLAN设置无线网络,只需要启动WLAN对应的类接可以了,不用打开Settings再点击进入WLAN中进行设置,Settings又是继承与SettinggsActivity,初始化界面应该是在父类SettinggsActivity里完成的,下面看一下启动流程:
我们在清单文件中找到WifiSettingsActivity的定义:
<!-- M: Add mcc | mnc to prevent some cases activity relaunch and cause duplicated WifiDialog ALPS02124573-->
<activity
android:name="Settings$WifiSettingsActivity"
android:label="@string/wifi_settings"
android:icon="@drawable/ic_settings_wireless"
android:configChanges="orientation|keyboardHidden|screenSize|mcc|mnc"
android:taskAffinity="com.android.settings"
android:parentActivityName="Settings">
<intent-filter android:priority="1">
<action android:name="android.settings.WIFI_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" />
<category android:name="android.intent.category.VOICE_LAUNCH" />
<category android:name="com.android.settings.SHORTCUT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.wifi.WifiSettings" />
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true" />
</activity>
其中有 meta-data 的标签使用,从这个标签的 key-value 来看,很明显可以认为WifiSettings的具体实现应该是由 WifiSettings 这个 Fragment 来布局渲染的。
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.wifi.WifiSettings" />
我们回到 SettingsActivity 中, onCreate() 方法如下:
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
/// M: ALPS02994518 If start by monkey, finish @ {
if (!isFinishing() && Utils.isMonkeyRunning()) {
Log.d(LOG_TAG, "finish due to monkey user");
finish();
return;
}
/// @ }
Log.d(LOG_TAG, "Starting onCreate");
long startTime = System.currentTimeMillis();
final FeatureFactory factory = FeatureFactory.getFactory(this);
//获得DashboardFeatureProviderImpl
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(this);
// Should happen before any call to getIntent()
//将子类在Manifest中com.android.settings.FRAGMENT_CLASS的值赋值给mFragmentClass
getMetaData();
//注意:这里getIntent()是自己重写过的方法,并不是Activity自带的getIntent()
final Intent intent = getIntent();
if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
}
// Getting Intent properties can only be done after the super.onCreate(...)
final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
final ComponentName cn = intent.getComponent();
final String className = cn.getClassName();//子类名
//如果是Settings界面,即主界面
mIsShowingDashboard = className.equals(Settings.class.getName());
可以看到,进入 oncreate 里有个 getMetaData(), 这和我们之前看到的清单文件里的meta似乎有某种联系,点进去看,代码如下:
public static final String META_DATA_KEY_FRAGMENT_CLASS =
"com.android.settings.FRAGMENT_CLASS";
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());
}
}
可以看到,这个函数的主要作用就是从 Activity 标签中获取 meta-data 标签中key为 com.android.settings.FRAGMENT_CLASS 的值,并将其赋值给 mFragmentClass 这个私有