经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
分析Android 11.0Settings源码之主界面加载
来源:jb51  时间:2021/4/12 19:00:22  对本文有异议

本篇主要记录AndroidR Settings源码主界面加载流程,方便后续工作调试其流程。

Settings代码路径:

packages/app/Settings/

Settings代码获取:

Setting 源码下载地址:https://github.com/aosp-mirror/platform_packages_apps_settings
git地址:https://github.com/aosp-mirror/platform_packages_apps_settings.git

主界面加载:

首先我们来看 Settings 模块中的 AndroidManifest.xml 文件,找到默认启动入口Activity信息:

  1. <activity android:name=".homepage.SettingsHomepageActivity"
  2. android:label="@string/settings_label_launcher"
  3. android:theme="@style/Theme.Settings.Home"
  4. android:taskAffinity="com.android.settings.root"
  5. android:launchMode="singleTask"
  6. android:configChanges="keyboard|keyboardHidden">
  7. <intent-filter android:priority="1">
  8. <action android:name="android.settings.SETTINGS" />
  9. <category android:name="android.intent.category.DEFAULT" />
  10. </intent-filter>
  11. <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
  12. android:value="true" />
  13. </activity>
  14. //activity-alias可用来设置某个Activity的快捷入口,可以放在桌面上或者通过该别名被其他组件快速调起。
  15. //android:targetActivity为目标Activity.
  16. <!-- Alias for launcher activity only, as this belongs to each profile. -->
  17. <activity-alias android:name="Settings"
  18. android:label="@string/settings_label_launcher"
  19. android:taskAffinity="com.android.settings.root"
  20. android:launchMode="singleTask"
  21. android:targetActivity=".homepage.SettingsHomepageActivity">
  22. <intent-filter>
  23. <action android:name="android.intent.action.MAIN" />
  24. <category android:name="android.intent.category.DEFAULT" />
  25. <category android:name="android.intent.category.LAUNCHER" />
  26. </intent-filter>
  27. <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
  28. </activity-alias>

可以看到Settings的桌面图标启动的主界面是Settings.java,但其xml定义了targetActivity属性,实质应是SettingsHomepageActivity.java,从onCreate()方法开始:

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4.  
  5. setContentView(R.layout.settings_homepage_container);
  6. final View root = findViewById(R.id.settings_homepage_container);
  7. root.setSystemUiVisibility(
  8. View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
  9.  
  10. setHomepageContainerPaddingTop();
  11.  
  12. final Toolbar toolbar = findViewById(R.id.search_action_bar);
  13. FeatureFactory.getFactory(this).getSearchFeatureProvider()
  14. .initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE);
  15.  
  16. final ImageView avatarView = findViewById(R.id.account_avatar);
  17. getLifecycle().addObserver(new AvatarViewMixin(this, avatarView));
  18. getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
  19.  
  20. if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
  21. // Only allow contextual feature on high ram devices.
  22. showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content);
  23. }
  24. showFragment(new TopLevelSettings(), R.id.main_content);
  25. ((FrameLayout) findViewById(R.id.main_content))
  26. .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
  27. }

可以看到主界面的layout为settings_homepage_container.xml:

  1. <androidx.coordinatorlayout.widget.CoordinatorLayout
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. android:id="@+id/settings_homepage_container"
  5. android:fitsSystemWindows="true"
  6. android:layout_width="match_parent"
  7. android:layout_height="match_parent">
  8.  
  9. <androidx.core.widget.NestedScrollView
  10. android:id="@+id/main_content_scrollable_container"
  11. android:layout_width="match_parent"
  12. android:layout_height="match_parent"
  13. app:layout_behavior="com.android.settings.widget.FloatingAppBarScrollingViewBehavior">
  14.  
  15. <LinearLayout
  16. android:id="@+id/homepage_container"
  17. android:layout_width="match_parent"
  18. android:layout_height="wrap_content"
  19. android:orientation="vertical">
  20.  
  21. <FrameLayout
  22. android:id="@+id/contextual_cards_content"
  23. android:layout_width="match_parent"
  24. android:layout_height="wrap_content"
  25. android:layout_marginStart="@dimen/contextual_card_side_margin"
  26. android:layout_marginEnd="@dimen/contextual_card_side_margin"/>
  27.  
  28. <FrameLayout
  29. android:id="@+id/main_content"
  30. android:layout_width="match_parent"
  31. android:layout_height="wrap_content"
  32. android:animateLayoutChanges="true"
  33. android:background="?android:attr/windowBackground"/>
  34.  
  35. </LinearLayout>
  36. </androidx.core.widget.NestedScrollView>
  37.  
  38. <com.google.android.material.appbar.AppBarLayout
  39. android:layout_width="match_parent"
  40. android:layout_height="wrap_content"
  41. android:touchscreenBlocksFocus="false"
  42. android:keyboardNavigationCluster="false">
  43. <include layout="@layout/search_bar"/>
  44. </com.google.android.material.appbar.AppBarLayout>
  45. </androidx.coordinatorlayout.widget.CoordinatorLayout>

主界面布局中主要包含两部分:一个顶部快捷搜索栏,一个Id为main_content的FrameLayout就是用来显示主设置内容的,即Settings的一级菜单项界面。
回到onCreate()方法:

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

可以看到一级菜单启动的是TopLevelSettings,TopLevelSettings继承于DashboardFragment.java:

  1. public class TopLevelSettings extends DashboardFragment implements
  2. PreferenceFragmentCompat.OnPreferenceStartFragmentCallback

TopLevelSettings的构造方法:

  1. public TopLevelSettings() {
  2. final Bundle args = new Bundle();
  3. // Disable the search icon because this page uses a full search view in actionbar.
  4. args.putBoolean(NEED_SEARCH_ICON_IN_ACTION_BAR, false);
  5. setArguments(args);
  6. }

可以看到通过构造方法传递了一个参数,从注释中可以看出,该参数的用意是由于主界面使用完整的搜索视图所以在主界面的actionbar中隐藏了搜索图标。然后再根据framgments生命周期先来看onAttach()方法:

  1. @Override
  2. public void onAttach(Context context) {
  3. super.onAttach(context);
  4. use(SupportPreferenceController.class).setActivity(getActivity());
  5. }

调用父类DashboardFragment.java的onAttach()方法:

  1. @Override
  2. public void onAttach(Context context) {
  3. super.onAttach(context);
  4. mSuppressInjectedTileKeys = Arrays.asList(context.getResources().getStringArray(
  5. R.array.config_suppress_injected_tile_keys));
  6. mDashboardFeatureProvider = FeatureFactory.getFactory(context).
  7. getDashboardFeatureProvider(context);
  8. // Load preference controllers from code
  9. final List<AbstractPreferenceController> controllersFromCode =
  10. createPreferenceControllers(context);
  11. // Load preference controllers from xml definition
  12. final List<BasePreferenceController> controllersFromXml = PreferenceControllerListHelper
  13. .getPreferenceControllersFromXml(context, getPreferenceScreenResId());
  14. // Filter xml-based controllers in case a similar controller is created from code already.
  15. final List<BasePreferenceController> uniqueControllerFromXml =
  16. PreferenceControllerListHelper.filterControllers(
  17. controllersFromXml, controllersFromCode);
  18.  
  19. // Add unique controllers to list.
  20. if (controllersFromCode != null) {
  21. mControllers.addAll(controllersFromCode);
  22. }
  23. mControllers.addAll(uniqueControllerFromXml);
  24.  
  25. // And wire up with lifecycle.
  26. final Lifecycle lifecycle = getSettingsLifecycle();
  27. uniqueControllerFromXml.forEach(controller -> {
  28. if (controller instanceof LifecycleObserver) {
  29. lifecycle.addObserver((LifecycleObserver) controller);
  30. }
  31. });
  32.  
  33. // Set metrics category for BasePreferenceController.
  34. final int metricCategory = getMetricsCategory();
  35. mControllers.forEach(controller -> {
  36. if (controller instanceof BasePreferenceController) {
  37. ((BasePreferenceController) controller).setMetricsCategory(metricCategory);
  38. }
  39. });
  40.  
  41. mPlaceholderPreferenceController =
  42. new DashboardTilePlaceholderPreferenceController(context);
  43. mControllers.add(mPlaceholderPreferenceController);
  44. for (AbstractPreferenceController controller : mControllers) {
  45. addPreferenceController(controller);
  46. }
  47. }

通过方法注释可以得知此方法主要是完成preference controllers的加载。
DashboardFragment.java的onCreate()方法:

  1. @Override
  2. public void onCreate(Bundle icicle) {
  3. super.onCreate(icicle);
  4. // Set ComparisonCallback so we get better animation when list changes.
  5. getPreferenceManager().setPreferenceComparisonCallback(
  6. new PreferenceManager.SimplePreferenceComparisonCallback());
  7. if (icicle != null) {
  8. // Upon rotation configuration change we need to update preference states before any
  9. // editing dialog is recreated (that would happen before onResume is called).
  10. updatePreferenceStates();
  11. }
  12. }

设置ComparisonCallback,以便在列表更改时获得更好的动画效果。
第一次进入时,icicle为null,根据log定位发现,其后调用DashboardFragment.java的onCreatePreferences()方法:

  1. @Override
  2. public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
  3. checkUiBlocker(mControllers);
  4. refreshAllPreferences(getLogTag());
  5. mControllers.stream()
  6. .map(controller -> (Preference) findPreference(controller.getPreferenceKey()))
  7. .filter(Objects::nonNull)
  8. .forEach(preference -> {
  9. // Give all controllers a chance to handle click.
  10. preference.getExtras().putInt(CATEGORY, getMetricsCategory());
  11. });
  12. }

调用refreshAllPreferences():

  1. /**
  2. * Refresh all preference items, including both static prefs from xml, and dynamic items from
  3. * DashboardCategory.
  4. */
  5. private void refreshAllPreferences(final String tag) {
  6. final PreferenceScreen screen = getPreferenceScreen();
  7. // First remove old preferences.
  8. if (screen != null) {
  9. // Intentionally do not cache PreferenceScreen because it will be recreated later.
  10. screen.removeAll();
  11. }
  12.  
  13. // Add resource based tiles.
  14. displayResourceTiles();
  15.  
  16. refreshDashboardTiles(tag);
  17.  
  18. final Activity activity = getActivity();
  19. if (activity != null) {
  20. Log.d(tag, "All preferences added, reporting fully drawn");
  21. activity.reportFullyDrawn();
  22. }
  23.  
  24. updatePreferenceVisibility(mPreferenceControllers);
  25. }

刷新所有preference items,包括来自xml的静态preference和来自DashboardCategory的动态preference,静态xml定义的prefs(调用displayResourceTiles()方法),动态DashboardCategory动态加载(调用refreshDashboardTiles(TAG)方法,其中TAG为 “TopLevelSettings”)。
displayResourceTiles():此方法主要是从xml资源文件中加载显示prefs:

  1. /**
  2. * Displays resource based tiles.
  3. */
  4. private void displayResourceTiles() {
  5. final int resId = getPreferenceScreenResId();
  6. if (resId <= 0) {
  7. return;
  8. }
  9. addPreferencesFromResource(resId);
  10. final PreferenceScreen screen = getPreferenceScreen();
  11. screen.setOnExpandButtonClickListener(this);
  12. displayResourceTilesToScreen(screen);
  13. }
  14. /**
  15. * Perform {@link AbstractPreferenceController#displayPreference(PreferenceScreen)}
  16. * on all {@link AbstractPreferenceController}s.
  17. */
  18. protected void displayResourceTilesToScreen(PreferenceScreen screen) {
  19. mPreferenceControllers.values().stream().flatMap(Collection::stream).forEach(
  20. controller -> controller.displayPreference(screen));
  21. }

静态加载

首先调用getPreferenceScreenResId()方法获取所要加载的xml的ID,然后调用子类TopLevelSettings.java的getPreferenceScreenResId()方法:

  1. @Override
  2. protected int getPreferenceScreenResId() {
  3. return R.xml.top_level_settings;
  4. }

可以看到Settings主界面加载的xml文件是top_level_settings:

  1. <PreferenceScreen
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:settings="http://schemas.android.com/apk/res-auto"
  4. android:key="top_level_settings">
  5.  
  6. <Preference
  7. android:key="top_level_network"
  8. android:title="@string/network_dashboard_title"
  9. android:summary="@string/summary_placeholder"
  10. android:icon="@drawable/ic_homepage_network"
  11. android:order="-120"
  12. android:fragment="com.android.settings.network.NetworkDashboardFragment"
  13. settings:controller="com.android.settings.network.TopLevelNetworkEntryPreferenceController"/>
  14.  
  15. <Preference
  16. android:key="top_level_connected_devices"
  17. android:title="@string/connected_devices_dashboard_title"
  18. android:summary="@string/summary_placeholder"
  19. android:icon="@drawable/ic_homepage_connected_device"
  20. android:order="-110"
  21. android:fragment="com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment"
  22. settings:controller="com.android.settings.connecteddevice.TopLevelConnectedDevicesPreferenceController"/>
  23.  
  24. <Preference
  25. android:key="top_level_apps_and_notifs"
  26. android:title="@string/app_and_notification_dashboard_title"
  27. android:summary="@string/app_and_notification_dashboard_summary"
  28. android:icon="@drawable/ic_homepage_apps"
  29. android:order="-100"
  30. android:fragment="com.android.settings.applications.AppAndNotificationDashboardFragment"/>
  31.  
  32. <Preference
  33. android:key="top_level_battery"
  34. android:title="@string/power_usage_summary_title"
  35. android:summary="@string/summary_placeholder"
  36. android:icon="@drawable/ic_homepage_battery"
  37. android:fragment="com.android.settings.fuelgauge.PowerUsageSummary"
  38. android:order="-90"
  39. settings:controller="com.android.settings.fuelgauge.TopLevelBatteryPreferenceController"/>
  40.  
  41. <Preference
  42. android:key="top_level_display"
  43. android:title="@string/display_settings"
  44. android:summary="@string/summary_placeholder"
  45. android:icon="@drawable/ic_homepage_display"
  46. android:order="-80"
  47. android:fragment="com.android.settings.DisplaySettings"
  48. settings:controller="com.android.settings.display.TopLevelDisplayPreferenceController"/>
  49.  
  50. <Preference
  51. android:key="top_level_sound"
  52. android:title="@string/sound_settings"
  53. android:summary="@string/sound_dashboard_summary"
  54. android:icon="@drawable/ic_homepage_sound"
  55. android:order="-70"
  56. android:fragment="com.android.settings.notification.SoundSettings"/>
  57.  
  58. <Preference
  59. android:key="top_level_storage"
  60. android:title="@string/storage_settings"
  61. android:summary="@string/summary_placeholder"
  62. android:icon="@drawable/ic_homepage_storage"
  63. android:order="-60"
  64. android:fragment="com.android.settings.deviceinfo.StorageSettings"
  65. settings:controller="com.android.settings.deviceinfo.TopLevelStoragePreferenceController"/>
  66.  
  67. <Preference
  68. android:key="top_level_privacy"
  69. android:title="@string/privacy_dashboard_title"
  70. android:summary="@string/privacy_dashboard_summary"
  71. android:icon="@drawable/ic_homepage_privacy"
  72. android:order="-55"
  73. android:fragment="com.android.settings.privacy.PrivacyDashboardFragment"/>
  74.  
  75. <Preference
  76. android:key="top_level_location"
  77. android:title="@string/location_settings_title"
  78. android:summary="@string/location_settings_loading_app_permission_stats"
  79. android:icon="@drawable/ic_homepage_location"
  80. android:order="-50"
  81. android:fragment="com.android.settings.location.LocationSettings"
  82. settings:controller="com.android.settings.location.TopLevelLocationPreferenceController"/>
  83.  
  84. <Preference
  85. android:key="top_level_security"
  86. android:title="@string/security_settings_title"
  87. android:summary="@string/summary_placeholder"
  88. android:icon="@drawable/ic_homepage_security"
  89. android:order="-40"
  90. android:fragment="com.android.settings.security.SecuritySettings"
  91. settings:controller="com.android.settings.security.TopLevelSecurityEntryPreferenceController"/>
  92.  
  93. <Preference
  94. android:key="top_level_accounts"
  95. android:title="@string/account_dashboard_title"
  96. android:summary="@string/summary_placeholder"
  97. android:icon="@drawable/ic_homepage_accounts"
  98. android:order="-30"
  99. android:fragment="com.android.settings.accounts.AccountDashboardFragment"
  100. settings:controller="com.android.settings.accounts.TopLevelAccountEntryPreferenceController"/>
  101.  
  102. <Preference
  103. android:key="top_level_accessibility"
  104. android:title="@string/accessibility_settings"
  105. android:summary="@string/accessibility_settings_summary"
  106. android:icon="@drawable/ic_homepage_accessibility"
  107. android:order="-20"
  108. android:fragment="com.android.settings.accessibility.AccessibilitySettings"
  109. settings:controller="com.android.settings.accessibility.TopLevelAccessibilityPreferenceController"/>
  110.  
  111. <Preference
  112. android:key="top_level_system"
  113. android:title="@string/header_category_system"
  114. android:summary="@string/system_dashboard_summary"
  115. android:icon="@drawable/ic_homepage_system_dashboard"
  116. android:order="10"
  117. android:fragment="com.android.settings.system.SystemDashboardFragment"/>
  118.  
  119. <Preference
  120. android:key="top_level_about_device"
  121. android:title="@string/about_settings"
  122. android:summary="@string/summary_placeholder"
  123. android:icon="@drawable/ic_homepage_about"
  124. android:order="20"
  125. android:fragment="com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment"
  126. settings:controller="com.android.settings.deviceinfo.aboutphone.TopLevelAboutDevicePreferenceController"/>
  127.  
  128. <Preference
  129. android:key="top_level_support"
  130. android:summary="@string/support_summary"
  131. android:title="@string/page_tab_title_support"
  132. android:icon="@drawable/ic_homepage_support"
  133. android:order="100"
  134. settings:controller="com.android.settings.support.SupportPreferenceController"/>
  135.  
  136. </PreferenceScreen>

可以看到主要配置的是一些Preference菜单项如网络和互联网、已连接的设备、应用和通知、电池等等,Preference的配置含义:

  • key:唯一性ID;
  • title:标题;
  • summary:简介;
  • ico:图标;
  • order:加载显示优先级,order为负时,绝对值越高,界面显示越靠前;order为正时,值越高,显示越靠后;
  • fragment:点击此preference所跳转的fragment界面;
  • controller:控制管理类。

动态加载

refreshDashboardTiles

总结:

  1. Settings的主Activity实质实现是在SettingsHomepageActivity.java内;
  2. Settings的主界面设置item的显示是在fragment上,fragment为TopLevelSettings.java,加载显示的布局为top_level_settings.xml;
  3. Settings主界面设置项item的加载显示主要分为两部分,一部分是xml定义的静态加载,xml为top_level_settings.xml;一部分是DashboardCategory来获取动态加载;
  4. 每个设置项item均为一个preference,通过xml定义加载时,必须要有一个controller,可以是在xml中定义"settings:controller"属性声明,名称必须与类的包名路径相同;也可直接在相关fragment中实现createPreferenceControllers()方法去调用构造相关controller。此二者存其一即可。
  5. xml中配置preference时,必须定义”android:key“属性;

以上就是分析Android 11.0Settings源码之主界面加载的详细内容,更多关于Android 11.0Settings源码的资料请关注w3xue其它相关文章!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号