写点什么

Android 11 Settings 源码入门,flutter 安装

用户头像
Android架构
关注
发布于: 10 小时前
  • 系统设置首页

  • 系统设置其他界面

  • 数据控制

一,系统设置首页

Android 系统设置的主界面是com.android.settings.Settings, 但是它只是一个activity-alias, 指向的是.homepage.SettingsHomepageActivity


<activity-alias android:name="Settings"android:label="@string/settings_label_launcher"android:taskAffinity="com.android.settings.root"android:launchMode="singleTask"android:targetActivity=".homepage.SettingsHomepageActivity"><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>


需要注意的是通过命令(adb shell "dumpsys window | grep mCurrentFocus")查看手机顶层 activity 时,打印出的不是 targetActivity,而是这个 activity-alias.


.homepage.SettingsHomepageActivity中的逻辑并不复杂,直接加载了TopLevelSettings这个 Fragment


showFragment(new TopLevelSettings(), R.id.main_content);


TopLevelSettings通过AndroidXPreference来展示设置项列表,设置项列表的内容通过静态配置+动态添加的方式获取。

1,静态配置

所谓静态配置就是通过 xml 来配置。 如果你还不了解Preference,可以移步:[www.jianshu.com/p/348eb0928…](


) 简单了解一下


TopLevelSettings继承自抽象类DashboardFragment, 实现抽象方法getPreferenceScreenResId()并返回 preference 的配置文件即可完成静态配置。


@Overrideprotected int getPreferenceScreenResId() {return R.xml.top_level_settings;}


top_level_settings中配置了页面需要展示的配置项:


<PreferenceScreenxmlns:android="http://schemas.android.com/apk/res/android"xmlns:settings="http://schemas.android.com/apk/res-auto"android:key="top_level_settings">


<Preferenceandroid:key="top_level_network"android:title="@string/network_dashboard_title"android:summary="@string/summary_placeholder"android:icon="@drawable/ic_homepage_network"android:order="-120"android:fragment="com.android.settings.network.NetworkDashboardFragment"settings:controller="com.android.settings.network.TopLevelNetworkEntryPreferenceController"/>


<Preferenceandroid:key="top_level_connected_devices"android:title="@string/connected_devices_dashboard_title"android:summary="@string/summary_placeholder"android:icon="@drawable/ic_homepage_connected_device"android:order="-110"android:fragment="com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment"settings:controller="com.android.settings.connecteddevice.TopLevelConnectedDevicesPreferenceController"/>


...


</PreferenceScreen>


其中:


  • key:该配置项的主键

  • title:配置项的标题

  • summary:概要标题下面的文字

  • icon:前面的图标

  • order:用来做排序的,值越小则排行越靠前

  • fragment:点击该 item 要跳转的界面

  • controller:该 item 的控制器,控制它的内容展示,是否可用,也可以控制它的点击事件

2,动态添加

动态获取是根据特殊的 action 标记,通过 packageManger 查询系统中安装的符合对应 action 的应用,将其动态添加到列表中。


例如:网络流量监控,存储空间管理,默认应用等配置项都是动态添加的。


具体实现可以参看文章:[MTK 快霸是如何动态加载的](


)

二,系统设置其他界面

系统设置中除了.homepage.SettingsHomepageActivity,其他大部分的 Activity 都定义在Settings中, 并且继承自SettingsActivity, 但其中并没有实现任何逻辑。因此,这些 Activity 的逻辑都是在SettingsActivity中实现。


/**


  • Top-level Settings activity*/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 CreateShortcutActivity extends SettingsActivity { / empty / }public static class FaceSettingsActivity extends SettingsActivity { / empty / }public static class FingerprintSettingsActivity extends SettingsActivity { / empty */ }...}


这些 Activity 中并没有实现任何逻辑,那它是怎么加载到自己应有的布局的呢?


在父类SettingsActivityonCreate()中:


@Overrideprotected void onCreate(Bundle savedState) {


...// Should happen before any call to getIntent()// 第一步 getMetaData();// 第二步 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);...// 第三步 launchSettingFragment(initialFr


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


agmentName, intent);


...}


跟着上面的三个步骤:


第一步


首先通过getMetaData()获取该 Activity 在 manifest 中配置的 fragment, 并赋值给mFragmentClass


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 recoveryLog.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());}}


那么 manifest 中是怎么配置的呢?如下:


<activity android:name=".Settings$WifiInfoActivity"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.DEVELOPMENT_PREFERENCE" /><category android:name="android.intent.category.DEFAULT" /></intent-filter><meta-data android:name="com.android.settings.FRAGMENT_CLASS"android:value="com.android.settings.wifi.WifiInfo" /></activity>


由此可知WifiInfoActivity这个 Acitivity 对应的 fragment 是:com.android.settings.wifi.WifiInfo


第二步


通过 getIntent()构造包含EXTRA_SHOW_FRAGMENT的 intent


public Intent getIntent() {Intent superIntent = super.getIntent();String startingFragment = getStartingFragmentClass(superIntent);// This is called from super.onCreate, isMultiPane() is not yet reliable// Do not use onIsHidingHeaders either, which relies itself on this methodif (startingFragment != null) {Intent modIntent = new Intent(superIntent);modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);Bundle args = superIntent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);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;}


/**


  • Checks if the component name in the intent is different from the Settings class and

  • returns the class name to load as a fragment.*/private String getStartingFragmentClass(Intent intent) {// 存在 mFragmentClass 则直接返回 if (mFragmentClass != null) return mFragmentClass;


String intentClass = intent.getComponent().getClassName();if (intentClass.equals(getClass().getName())) return null;


if ("com.android.settings.RunningServices".equals(intentClass)|| "com.android.settings.applications.StorageUse".equals(intentClass)) {// Old names of manage apps.intentClass = ManageApplications.class.getName();}


return intentClass;}


这里包含了mFragmentClass为空的情况,暂时先不管。


第三步

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android 11 Settings源码入门,flutter安装