本篇主要记录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
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<activity android:name=".homepage.SettingsHomepageActivity" android:label="@string/settings_label_launcher" android:theme="@style/Theme.Settings.Home" android:taskAffinity="com.android.settings.root" android:launchMode="singleTask" android:configChanges="keyboard|keyboardHidden"> <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-alias可用来设置某个Activity的快捷入口,可以放在桌面上或者通过该别名被其他组件快速调起。 //android:targetActivity为目标Activity. <!-- Alias for launcher activity only, as this belongs to each profile. --> <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>
可以看到Settings的桌面图标启动的主界面是Settings.java,但其xml定义了targetActivity属性,实质应是SettingsHomepageActivity.java,从onCreate()方法开始:
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@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.settings_homepage_container); final View root = findViewById(R.id.settings_homepage_container); root.setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); setHomepageContainerPaddingTop(); final Toolbar toolbar = findViewById(R.id.search_action_bar); FeatureFactory.getFactory(this).getSearchFeatureProvider() .initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE); final ImageView avatarView = findViewById(R.id.account_avatar); getLifecycle().addObserver(new AvatarViewMixin(this, avatarView)); getLifecycle().addObserver(new HideNonSystemOverlayMixin(this)); if (!getSystemService(ActivityManager.class).isLowRamDevice()) { // Only allow contextual feature on high ram devices. showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content); } showFragment(new TopLevelSettings(), R.id.main_content); ((FrameLayout) findViewById(R.id.main_content)) .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); }
可以看到主界面的layout为settings_homepage_container.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
38
39
40
41
42
43
44
45<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/settings_homepage_container" android:fitsSystemWindows="true" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.core.widget.NestedScrollView android:id="@+id/main_content_scrollable_container" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="com.android.settings.widget.FloatingAppBarScrollingViewBehavior"> <LinearLayout android:id="@+id/homepage_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <FrameLayout android:id="@+id/contextual_cards_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="@dimen/contextual_card_side_margin" android:layout_marginEnd="@dimen/contextual_card_side_margin"/> <FrameLayout android:id="@+id/main_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:animateLayoutChanges="true" android:background="?android:attr/windowBackground"/> </LinearLayout> </androidx.core.widget.NestedScrollView> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:touchscreenBlocksFocus="false" android:keyboardNavigationCluster="false"> <include layout="@layout/search_bar"/> </com.google.android.material.appbar.AppBarLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
主界面布局中主要包含两部分:一个顶部快捷搜索栏,一个Id为main_content的FrameLayout就是用来显示主设置内容的,即Settings的一级菜单项界面。
回到onCreate()方法:
1showFragment(new TopLevelSettings(), R.id.main_content);
可以看到一级菜单启动的是TopLevelSettings,TopLevelSettings继承于DashboardFragment.java:
1
2public class TopLevelSettings extends DashboardFragment implements PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
TopLevelSettings的构造方法:
1
2
3
4
5
6public TopLevelSettings() { final Bundle args = new Bundle(); // Disable the search icon because this page uses a full search view in actionbar. args.putBoolean(NEED_SEARCH_ICON_IN_ACTION_BAR, false); setArguments(args); }
可以看到通过构造方法传递了一个参数,从注释中可以看出,该参数的用意是由于主界面使用完整的搜索视图所以在主界面的actionbar中隐藏了搜索图标。然后再根据framgments生命周期先来看onAttach()方法:
1
2
3
4
5@Override public void onAttach(Context context) { super.onAttach(context); use(SupportPreferenceController.class).setActivity(getActivity()); }
调用父类DashboardFragment.java的onAttach()方法:
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
38
39
40
41
42
43
44
45
46
47@Override public void onAttach(Context context) { super.onAttach(context); mSuppressInjectedTileKeys = Arrays.asList(context.getResources().getStringArray( R.array.config_suppress_injected_tile_keys)); mDashboardFeatureProvider = FeatureFactory.getFactory(context). getDashboardFeatureProvider(context); // Load preference controllers from code final List<AbstractPreferenceController> controllersFromCode = createPreferenceControllers(context); // Load preference controllers from xml definition final List<BasePreferenceController> controllersFromXml = PreferenceControllerListHelper .getPreferenceControllersFromXml(context, getPreferenceScreenResId()); // Filter xml-based controllers in case a similar controller is created from code already. final List<BasePreferenceController> uniqueControllerFromXml = PreferenceControllerListHelper.filterControllers( controllersFromXml, controllersFromCode); // Add unique controllers to list. if (controllersFromCode != null) { mControllers.addAll(controllersFromCode); } mControllers.addAll(uniqueControllerFromXml); // And wire up with lifecycle. final Lifecycle lifecycle = getSettingsLifecycle(); uniqueControllerFromXml.forEach(controller -> { if (controller instanceof LifecycleObserver) { lifecycle.addObserver((LifecycleObserver) controller); } }); // Set metrics category for BasePreferenceController. final int metricCategory = getMetricsCategory(); mControllers.forEach(controller -> { if (controller instanceof BasePreferenceController) { ((BasePreferenceController) controller).setMetricsCategory(metricCategory); } }); mPlaceholderPreferenceController = new DashboardTilePlaceholderPreferenceController(context); mControllers.add(mPlaceholderPreferenceController); for (AbstractPreferenceController controller : mControllers) { addPreferenceController(controller); } }
通过方法注释可以得知此方法主要是完成preference controllers的加载。
DashboardFragment.java的onCreate()方法:
1
2
3
4
5
6
7
8
9
10
11
12@Override public void onCreate(Bundle icicle) { super.onCreate(icicle); // Set ComparisonCallback so we get better animation when list changes. getPreferenceManager().setPreferenceComparisonCallback( new PreferenceManager.SimplePreferenceComparisonCallback()); if (icicle != null) { // Upon rotation configuration change we need to update preference states before any // editing dialog is recreated (that would happen before onResume is called). updatePreferenceStates(); } }
设置ComparisonCallback,以便在列表更改时获得更好的动画效果。
第一次进入时,icicle为null,根据log定位发现,其后调用DashboardFragment.java的onCreatePreferences()方法:
1
2
3
4
5
6
7
8
9
10
11
12@Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { checkUiBlocker(mControllers); refreshAllPreferences(getLogTag()); mControllers.stream() .map(controller -> (Preference) findPreference(controller.getPreferenceKey())) .filter(Objects::nonNull) .forEach(preference -> { // Give all controllers a chance to handle click. preference.getExtras().putInt(CATEGORY, getMetricsCategory()); }); }
调用refreshAllPreferences():
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/** * Refresh all preference items, including both static prefs from xml, and dynamic items from * DashboardCategory. */ private void refreshAllPreferences(final String tag) { final PreferenceScreen screen = getPreferenceScreen(); // First remove old preferences. if (screen != null) { // Intentionally do not cache PreferenceScreen because it will be recreated later. screen.removeAll(); } // Add resource based tiles. displayResourceTiles(); refreshDashboardTiles(tag); final Activity activity = getActivity(); if (activity != null) { Log.d(tag, "All preferences added, reporting fully drawn"); activity.reportFullyDrawn(); } updatePreferenceVisibility(mPreferenceControllers); }
刷新所有preference items,包括来自xml的静态preference和来自DashboardCategory的动态preference,静态xml定义的prefs(调用displayResourceTiles()方法),动态DashboardCategory动态加载(调用refreshDashboardTiles(TAG)方法,其中TAG为 “TopLevelSettings”)。
displayResourceTiles():此方法主要是从xml资源文件中加载显示prefs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/** * Displays resource based tiles. */ private void displayResourceTiles() { final int resId = getPreferenceScreenResId(); if (resId <= 0) { return; } addPreferencesFromResource(resId); final PreferenceScreen screen = getPreferenceScreen(); screen.setOnExpandButtonClickListener(this); displayResourceTilesToScreen(screen); } /** * Perform {@link AbstractPreferenceController#displayPreference(PreferenceScreen)} * on all {@link AbstractPreferenceController}s. */ protected void displayResourceTilesToScreen(PreferenceScreen screen) { mPreferenceControllers.values().stream().flatMap(Collection::stream).forEach( controller -> controller.displayPreference(screen)); }
静态加载
首先调用getPreferenceScreenResId()方法获取所要加载的xml的ID,然后调用子类TopLevelSettings.java的getPreferenceScreenResId()方法:
1
2
3
4@Override protected int getPreferenceScreenResId() { return R.xml.top_level_settings; }
可以看到Settings主界面加载的xml文件是top_level_settings:
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:settings="http://schemas.android.com/apk/res-auto" android:key="top_level_settings"> <Preference android: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"/> <Preference android: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"/> <Preference android:key="top_level_apps_and_notifs" android:title="@string/app_and_notification_dashboard_title" android:summary="@string/app_and_notification_dashboard_summary" android:icon="@drawable/ic_homepage_apps" android:order="-100" android:fragment="com.android.settings.applications.AppAndNotificationDashboardFragment"/> <Preference android:key="top_level_battery" android:title="@string/power_usage_summary_title" android:summary="@string/summary_placeholder" android:icon="@drawable/ic_homepage_battery" android:fragment="com.android.settings.fuelgauge.PowerUsageSummary" android:order="-90" settings:controller="com.android.settings.fuelgauge.TopLevelBatteryPreferenceController"/> <Preference android:key="top_level_display" android:title="@string/display_settings" android:summary="@string/summary_placeholder" android:icon="@drawable/ic_homepage_display" android:order="-80" android:fragment="com.android.settings.DisplaySettings" settings:controller="com.android.settings.display.TopLevelDisplayPreferenceController"/> <Preference android:key="top_level_sound" android:title="@string/sound_settings" android:summary="@string/sound_dashboard_summary" android:icon="@drawable/ic_homepage_sound" android:order="-70" android:fragment="com.android.settings.notification.SoundSettings"/> <Preference android:key="top_level_storage" android:title="@string/storage_settings" android:summary="@string/summary_placeholder" android:icon="@drawable/ic_homepage_storage" android:order="-60" android:fragment="com.android.settings.deviceinfo.StorageSettings" settings:controller="com.android.settings.deviceinfo.TopLevelStoragePreferenceController"/> <Preference android:key="top_level_privacy" android:title="@string/privacy_dashboard_title" android:summary="@string/privacy_dashboard_summary" android:icon="@drawable/ic_homepage_privacy" android:order="-55" android:fragment="com.android.settings.privacy.PrivacyDashboardFragment"/> <Preference android:key="top_level_location" android:title="@string/location_settings_title" android:summary="@string/location_settings_loading_app_permission_stats" android:icon="@drawable/ic_homepage_location" android:order="-50" android:fragment="com.android.settings.location.LocationSettings" settings:controller="com.android.settings.location.TopLevelLocationPreferenceController"/> <Preference android:key="top_level_security" android:title="@string/security_settings_title" android:summary="@string/summary_placeholder" android:icon="@drawable/ic_homepage_security" android:order="-40" android:fragment="com.android.settings.security.SecuritySettings" settings:controller="com.android.settings.security.TopLevelSecurityEntryPreferenceController"/> <Preference android:key="top_level_accounts" android:title="@string/account_dashboard_title" android:summary="@string/summary_placeholder" android:icon="@drawable/ic_homepage_accounts" android:order="-30" android:fragment="com.android.settings.accounts.AccountDashboardFragment" settings:controller="com.android.settings.accounts.TopLevelAccountEntryPreferenceController"/> <Preference android:key="top_level_accessibility" android:title="@string/accessibility_settings" android:summary="@string/accessibility_settings_summary" android:icon="@drawable/ic_homepage_accessibility" android:order="-20" android:fragment="com.android.settings.accessibility.AccessibilitySettings" settings:controller="com.android.settings.accessibility.TopLevelAccessibilityPreferenceController"/> <Preference android:key="top_level_system" android:title="@string/header_category_system" android:summary="@string/system_dashboard_summary" android:icon="@drawable/ic_homepage_system_dashboard" android:order="10" android:fragment="com.android.settings.system.SystemDashboardFragment"/> <Preference android:key="top_level_about_device" android:title="@string/about_settings" android:summary="@string/summary_placeholder" android:icon="@drawable/ic_homepage_about" android:order="20" android:fragment="com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment" settings:controller="com.android.settings.deviceinfo.aboutphone.TopLevelAboutDevicePreferenceController"/> <Preference android:key="top_level_support" android:summary="@string/support_summary" android:title="@string/page_tab_title_support" android:icon="@drawable/ic_homepage_support" android:order="100" settings:controller="com.android.settings.support.SupportPreferenceController"/> </PreferenceScreen>
可以看到主要配置的是一些Preference菜单项如网络和互联网、已连接的设备、应用和通知、电池等等,Preference的配置含义:
- key:唯一性ID;
- title:标题;
- summary:简介;
- ico:图标;
- order:加载显示优先级,order为负时,绝对值越高,界面显示越靠前;order为正时,值越高,显示越靠后;
- fragment:点击此preference所跳转的fragment界面;
- controller:控制管理类。
动态加载
refreshDashboardTiles
总结:
- Settings的主Activity实质实现是在SettingsHomepageActivity.java内;
- Settings的主界面设置item的显示是在fragment上,fragment为TopLevelSettings.java,加载显示的布局为top_level_settings.xml;
- Settings主界面设置项item的加载显示主要分为两部分,一部分是xml定义的静态加载,xml为top_level_settings.xml;一部分是DashboardCategory来获取动态加载;
- 每个设置项item均为一个preference,通过xml定义加载时,必须要有一个controller,可以是在xml中定义"settings:controller"属性声明,名称必须与类的包名路径相同;也可直接在相关fragment中实现createPreferenceControllers()方法去调用构造相关controller。此二者存其一即可。
- xml中配置preference时,必须定义”android:key“属性;
以上就是分析Android 11.0Settings源码之主界面加载的详细内容,更多关于Android 11.0Settings源码的资料请关注靠谱客其它相关文章!
最后
以上就是粗犷自行车最近收集整理的关于分析Android 11.0Settings源码之主界面加载的全部内容,更多相关分析Android内容请搜索靠谱客的其他文章。
发表评论 取消回复