android 应用程序分析之settings,Android 8.0 Settings流程分析与变动-Go语言中文社区

开! 场!  白! 好! 难! 写!

一,相比Android Settings 7.0

如下图,在7.0的基础上,去掉了7.0新加的侧滑菜单(可能是觉得有点鸡肋吧)。多加了一级页面,把原来类别标题变成的第一级菜单的子项。在代码架构也稍加变动,并引入架构组件之LifeCycle(生命周期感知,本文不作介绍)。

0c27cfe749530730ff01a9a65a819acc.png

二,第一级菜单的加载

浏览源码,大多数我们从程序的AndroidManifest.xml入手,这次也不列外。

packagesappsSettingsAndroidManifest.xml:

android:taskAffinity="com.android.settings"

android:label="@string/settings_label_launcher"

android:launchMode="singleTask"

android:targetActivity="Settings">

找到所属的类,Settings.java。但打开Settings.java类看,除了大量静态类继承SettingsActivity,就没什么东西了。那再去父类SettingsActivity.java找找。

packagesappsSettingssrccomandroidsettingsSettingsActivity.java:

首先当然是onCreate()->@Override

protected void onCreate(Bundle savedState) {

super.onCreate(savedState);

long startTime = System.currentTimeMillis();

//工厂类实现方法com.android.settings.overlay.FeatureFactoryImpl.java

final FeatureFactory factory = FeatureFactory.getFactory(this);

//获取菜单信息的工厂类,实现类为DashboardFeatureProviderImpl.java

mDashboardFeatureProvider = factory.getDashboardFeatureProvider(this);

mMetricsFeatureProvider = factory.getMetricsFeatureProvider();

// 从intent信息中获取标签名为"com.android.settings.FRAGMENT_CLASS"的值(下文用于加载Fragment的类名)

getMetaData();

... ...

//获取上面getMetaData()得到的类名

final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);

//是否为快捷进入方式(如从其它的应用进入Settings的某个设置项)

mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||

intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);

... ...

//当前类是否为Settings.class,即进入方式为点击launcher上的图标进入的主设置界面

mIsShowingDashboard = className.equals(Settings.class.getName());

... ...

setContentView(mIsShowingDashboard ?

R.layout.settings_main_dashboard : R.layout.settings_main_prefs);

mContent = findViewById(R.id.main_content);

getFragmentManager().addOnBackStackChangedListener(this);

if (savedState != null) {

... ...

} else {

//加载布局

launchSettingFragment(initialFragmentName, isSubSettings, intent);

}

... ...

}

launchSettingFragment()->@VisibleForTesting

void launchSettingFragment(String initialFragmentName, boolean isSubSettings, Intent intent) {

if (!mIsShowingDashboard && initialFragmentName != null) {

... ...

switchToFragment(initialFragmentName, initialArguments, true, false,

mInitialTitleResId, mInitialTitle, false);

} else {

// Show search icon as up affordance if we are displaying the main Dashboard

mDisplayHomeAsUpEnabled = true;

mInitialTitleResId = R.string.dashboard_title;

//进入主页走的这里,替换目标Fragment

switchToFragment(DashboardSummary.class.getName(), null /* args */, false, false,

mInitialTitleResId, mInitialTitle, false);

}

}

继续,我们看看DashboardSummary.java,对于它我们主要是想知道它的数据加载,它是怎么加载自己的子项的。

packagesappsSettingssrccomandroidsettingsdashboardDashboardSummary.java:

对子项的数据获取在updateCategoryAndSuggestion()中得到实现。@VisibleForTesting

void updateCategoryAndSuggestion(List suggestions) {

final Activity activity = getActivity();

if (activity == null) {

return;

}

/*根据"com.android.settings.category"的值查询子项数据,这里的值为"com.android.settings.category.ia.homepage"。

具体获取办法追踪到frameworksbasepackagesSettingsLibsrccomandroidsettingslibdrawerTileUtils.java中。

通过PackageManager查询所有在AndroidManifest.xml中定义中含有该值的类。注意:会过滤掉非系统级应用的数据!

有兴趣的自行研究,这里不深究。*/

final DashboardCategory category = mDashboardFeatureProvider.getTilesForCategory(

CategoryKey.CATEGORY_HOMEPAGE);

if (category == null) {

return;

}

mSummaryLoader.updateSummaryToCache(category);

if (suggestions != null) {

mAdapter.setCategoriesAndSuggestions(category, suggestions);

} else {

//数据的绑定在适配器中,->packagesappsSettingssrccomandroidsettingsdashboardDashboardAdapter.java

mAdapter.setCategory(category);

}

}

对于第一级菜单的加载。在AndroidManifest.xml中的配置如下列图:

9c5a3b3a5dc69201ea2cc4394226d2d4.png

三,第二级菜单的加载

以上我们知道第一级菜单是完全动态的加载,但二级菜单则是动态加载和静态xml布局文件,就拿“系统”这项为例。

packagesappsSettingsAndroidManifest.xml:

android:label="@string/header_category_system"

android:icon="@drawable/ic_settings_about">

android:value="com.android.settings.category.ia.homepage"/>

android:value="com.android.settings.system.SystemDashboardFragment"/>

android:resource="@string/system_dashboard_summary"/>

SystemDashboardFragment.java继承DashboardFragment.java。我们主要观察这个类。

packagesappsSettingssrccomandroidsettingsdashboardDashboardFragment.java:

1,静态加载部分:

静态加载部分的实现方法为displayResourceTiles()->/**

* Displays resource based tiles.

*/

private void displayResourceTiles() {

//获取xml布局文件的id(DashboardFragment.java实现该方法)

final int resId = getPreferenceScreenResId();

if (resId <= 0) {

return;

}

addPreferencesFromResource(resId);

final PreferenceScreen screen = getPreferenceScreen();

/** 实现布局文件中的子项控件的业务逻辑

* DashboardFragment.java的子类实现getPreferenceControllers()方法,该方法加载继承于AbstractPreferenceController.java的实现业务逻辑类

*/

Collection controllers = mPreferenceControllers.values();

for (AbstractPreferenceController controller : controllers) {

controller.displayPreference(screen);

}

}

2,动态加载部分:

动态加载部分的实现方法refreshDashboardTiles()->/**

* Refresh preference items backed by DashboardCategory.

*/

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)

void refreshDashboardTiles(final String TAG) {

final PreferenceScreen screen = getPreferenceScreen();

/* 获取子项

* getCategoryKey()从DashboardFragmentRegistry.PARENT_TO_CATEGORY_KEY_MAP中获取Category值。

* 该值通过类名获取

* 存:PARENT_TO_CATEGORY_KEY_MAP.put(SystemDashboardFragment.class.getName(), CategoryKey.CATEGORY_SYSTEM);

* CATEGORY_SYSTEM = "com.android.settings.category.ia.system";

*/

final DashboardCategory category =

mDashboardFeatureProvider.getTilesForCategory(getCategoryKey());

... ...

// Install dashboard tiles.

for (Tile tile : tiles) {

... ...

if (mDashboardTilePrefKeys.contains(key)) {

... ...

} else {

// Don't have this key, add it.

final Preference pref = new Preference(getPrefContext());

/*在这里进行绑定,加载

*packagesappsSettingssrccomandroidsettingsdashboardDashboardFeatureProviderImpl.java->bindPreferenceToTile()

*/

mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), getMetricsCategory(),

pref, tile, key, mPlaceholderPreferenceController.getOrder());

mProgressiveDisclosureMixin.addPreference(screen, pref);

mDashboardTilePrefKeys.add(key);

}

remove.remove(key);

}

// Finally remove tiles that are gone.

for (String key : remove) {

mDashboardTilePrefKeys.remove(key);

mProgressiveDisclosureMixin.removePreference(screen, key);

}

mSummaryLoader.setListening(true);

}

该文的Settings加载流程就差不多到这里了。

四,顺便说说

下拉菜单栏时长按设置图标进入设置,在系统项里面会多一个《系统界面调节工具》。那么这是怎么显示和隐藏的了?

frameworksbasepackagesSystemUIsrccomandroidsystemuitunerTunerService.java

->setTunerEnabled():public static final void setTunerEnabled(Context context, boolean enabled) {

//隐藏应用图标,隐藏某个组件启动也可以使用该方法

userContext(context).getPackageManager().setComponentEnabledSetting(

new ComponentName(context, TunerActivity.class),

enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED

: PackageManager.COMPONENT_ENABLED_STATE_DISABLED,

PackageManager.DONT_KILL_APP);

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值