android setting模块,android O版本 设置(Settings)模块总结--设置的一级界面的加载

本文详细解析了Android O版本设置模块中一级菜单的加载过程,包括DashboardSummary、DashboardFeatureProviderImpl和CategoryManager的角色。数据加载始于SettingsDrawerActivity的CategoriesUpdateTask异步任务,通过CategoryManager的reloadAllCategories()函数获取并处理数据,最终由DashboardSummary的onCategoriesChanged()方法刷新界面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

O版本的设置界面相对有N有了一些变化,O上面增加了顶级类别的菜单,而之前一些在一级菜单的则移动到了二级界面里面,

如"WIFI","移动网络"等之前是在一级界面的,而在O上则移动到了新菜单"网络和互联网"中,但是在数据在加载方面,并未做较大的变化.

(a)一级界面--顶级菜单的数据加载

在上一篇 <> 中有说到DashboardSummary是顶级菜单的容器,那么数据的获取和它也就有关系了:

DashboardSummary.java

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

final Activity activity = getActivity();

mDashboardFeatureProvider = FeatureFactory.getFactory(activity)

.getDashboardFeatureProvider(activity);

mSuggestionFeatureProvider = FeatureFactory.getFactory(activity)

.getSuggestionFeatureProvider(activity);

mSummaryLoader = new SummaryLoader(activity, CategoryKey.CATEGORY_HOMEPAGE);

mConditionManager = ConditionManager.get(activity, false);

getLifecycle().addObserver(mConditionManager);

mSuggestionParser = new SuggestionParser(activity,

activity.getSharedPreferences(SUGGESTIONS, 0), R.xml.suggestion_ordering);

mSuggestionsChecks = new SuggestionsChecks(getContext());

}

在DashboardSummary的onCreate函数中有获取的有两个很重要参数:mDashboardFeatureProvider,mSuggestionFeatureProvider.这两个是主要的数据提供者,mSuggestionFeatureProvider和mDashboardFeatureProvider的数据获取是有所不同的,这里就不再说明mSuggestionFeatureProvider了,重点说明下mDashboardFeatureProvider.

mDashboardFeatureProvider提供的数据是一级菜单如"电池","显示","网络和互联网"等,实现类为DashboardFeatureProviderImpl.java,而DashboardFeatureProviderImpl中的具体的数据是通过函数getTilesForCategory()从CategoryManager获取的.

public DashboardFeatureProviderImpl(Context context) {

mContext = context.getApplicationContext();

mCategoryManager = CategoryManager.get(context, getExtraIntentAction());

mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();

mPackageManager = context.getPackageManager();

}

@Override

public DashboardCategory getTilesForCategory(String key) {

return mCategoryManager.getTilesByCategory(mContext, key);

}

CategoryManager是SettingsLib这个静态包中公共类,可以看一下:

public static CategoryManager get(Context context, String action) {

if (sInstance == null) {

sInstance = new CategoryManager(context, action);

}

return sInstance;

}

CategoryManager(Context context, String action) {

mTileByComponentCache = new ArrayMap<>();

mCategoryByKeyMap = new ArrayMap<>();

mInterestingConfigChanges = new InterestingConfigChanges();

mInterestingConfigChanges.applyNewConfig(context.getResources());

mExtraAction = action;

}

可以看到CategoryManager是一个单例类型,这里就是真正的数据加载位置,加载是通过函数reloadAllCategories()调用tryInitCategories()获取的.

到这里为止,整个数据获取的流程已经清楚,但是数据加载是在哪里完成的呢,还要回到SettingsDrawerActivity中:

SettingsDrawerActivity.java

@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();

final Intent intent = getIntent();

if (intent != null && intent.getBooleanExtra(EXTRA_SHOW_MENU, false)) {

// Intent explicitly set to show menu.

showMenuIcon();

}

}

在SettingsDrawerActivity中注册了安装应用状态变化的广播接收器等,但是这里还进行了一个异步操作:

new CategoriesUpdateTask().execute();

private class CategoriesUpdateTask extends AsyncTask{

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();

}

}

在这个AsyncTask中,doInBackground()调用了CategoryManager的reloadAllCategories()函数,而onPostExecute则调用了接口CategoryListener的唯一方法onCategoriesChanged(),那么作为界面容器的DashboardSummary肯定重载了这个接口,实现了onCategoriesChanged()方法:

DashboardSummary

@Override

public void onCategoriesChanged() {

// Bypass rebuildUI() on the first call of onCategoriesChanged, since rebuildUI() happens

// in onViewCreated as well when app starts. But, on the subsequent calls we need to

// rebuildUI() because there might be some changes to suggestions and categories.

if (isOnCategoriesChangedCalled) {

rebuildUI();

}

isOnCategoriesChangedCalled = true;

}

DashboardSummary在方法onCategoriesChanged()中进行了界面的刷新,这里先不了解,后边再说,继续研究数据加载.

mCategoryManager.reloadAllCategories(SettingsDrawerActivity.this, getSettingPkg());

reloadAllCategories函数中调用了tryInitCategories(),此函数中是获取数据以及对数据的处理,来看下函数tryInitCategories:

private synchronized void tryInitCategories(Context context, boolean forceClearCache,

String settingPkg) {

if (mCategories == null) {

if (forceClearCache) {

mTileByComponentCache.clear();

}

//清除缓存

mCategoryByKeyMap.clear();

//数据获取

mCategories = TileUtils.getCategories(context, mTileByComponentCache,

false /* categoryDefinedInManifest */, mExtraAction, settingPkg);

//数据保存

for (DashboardCategory category : mCategories) {

mCategoryByKeyMap.put(category.key, category);

}

backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);

//数据排序清理重复等

sortCategories(context, mCategoryByKeyMap);

filterDuplicateTiles(mCategoryByKeyMap);

}

}

SettingsLib/src/com/android/settingslib/drawer/TileUtils.java

public static ListgetCategories(Context context,

Map, Tile> cache, boolean categoryDefinedInManifest,

String extraAction, String settingPkg) {

final long startTime = System.currentTimeMillis();

boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0)

!= 0;

ArrayListtiles = new ArrayList<>();

UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);

//此处是整个数据的获取

for (UserHandle user : userManager.getUserProfiles()) {

// TODO: Needs much optimization, too many PM queries going on here.

if (user.getIdentifier() == ActivityManager.getCurrentUser()) {

// Only add Settings for this user.

getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true,

settingPkg);

getTilesForAction(context, user, OPERATOR_SETTINGS, cache,

OPERATOR_DEFAULT_CATEGORY, tiles, false, true, settingPkg);

getTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,

MANUFACTURER_DEFAULT_CATEGORY, tiles, false, true, settingPkg);

}

if (setup) {

getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false,

settingPkg);

if (!categoryDefinedInManifest) {

getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false,

settingPkg);

if (extraAction != null) {

getTilesForAction(context, user, extraAction, cache, null, tiles, false,

settingPkg);

}

}

}

}

//按照进行meta-data android:name="com.android.settings.category"进行分类

HashMapcategoryMap = new HashMap<>();

for (Tile tile : tiles) {

DashboardCategory category = categoryMap.get(tile.category);

if (category == null) {

category = createCategory(context, tile.category, categoryDefinedInManifest);

if (category == null) {

Log.w(LOG_TAG, "Couldn't find category " + tile.category);

continue;

}

categoryMap.put(category.key, category);

}

category.addTile(tile);

}

//对分类进行排序

ArrayListcategories = new ArrayList<>(categoryMap.values());

for (DashboardCategory category : categories) {

Collections.sort(category.tiles, TILE_COMPARATOR);

}

Collections.sort(categories, CATEGORY_COMPARATOR);

if (DEBUG_TIMING) Log.d(LOG_TAG, "getCategories took "

+ (System.currentTimeMillis() - startTime) + " ms");

return categories;

}

在函数getCategories中,就逐个开始通过PackageManager获取包含有指定Action:

private static final String SETTINGS_ACTION =

"com.android.settings.action.SETTINGS";

private static final String OPERATOR_SETTINGS =

"com.android.settings.OPERATOR_APPLICATION_SETTING";

private static final String MANUFACTURER_SETTINGS =

"com.android.settings.MANUFACTURER_APPLICATION_SETTING";

private static final String EXTRA_SETTINGS_ACTION =

"com.android.settings.action.EXTRA_SETTINGS";

private static void getTilesForAction(Context context,

UserHandle user, String action, Map, Tile> addedCache,

String defaultCategory, ArrayListoutTiles, boolean requireSettings,

boolean usePriority, String settingPkg) {

//填充Intent

Intent intent = new Intent(action);

if (requireSettings) {

intent.setPackage(settingPkg);

}

getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles,

usePriority, true);

}

public static void getTilesForIntent(Context context, UserHandle user, Intent intent,

Map, Tile> addedCache, String defaultCategory, ListoutTiles,

boolean usePriority, boolean checkCategory) {

PackageManager pm = context.getPackageManager();

//通过PM 在已经安装的应用中获取指定Action的Activity信息.

Listresults = pm.queryIntentActivitiesAsUser(intent,

PackageManager.GET_META_DATA, user.getIdentifier());

for (ResolveInfo resolved : results) {

//此处是对非系统应用添加设置菜单做了限制的

if (!resolved.system) {

// Do not allow any app to add to settings, only system ones.

continue;

}

ActivityInfo activityInfo = resolved.activityInfo;

Bundle metaData = activityInfo.metaData;

String categoryKey = defaultCategory;

//通过meta-data android:name="com.android.settings.category"获取菜单分类.

//例如:com.android.settings.category.ia.homepage就是在一级目录

//例如:com.android.settings.category.ia.device显示在顶级菜单"设备链接"中

// Load category

if (checkCategory && ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY))

&& categoryKey == null) {

Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent "

+ intent + " missing metadata "

+ (metaData == null ? "" : EXTRA_CATEGORY_KEY));

continue;

} else {

categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);

}

Pairkey = new Pair(activityInfo.packageName,

activityInfo.name);

Tile tile = addedCache.get(key);

if (tile == null) {

tile = new Tile();

tile.intent = new Intent().setClassName(

activityInfo.packageName, activityInfo.name);

tile.category = categoryKey;

tile.priority = usePriority ? resolved.priority : 0;

tile.metaData = activityInfo.metaData;

//菜单的标题,图标,概要(summary),对应Activity的获取和赋值,这个可以自行分析下

updateTileData(context, tile, activityInfo, activityInfo.applicationInfo,

pm);

if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title);

addedCache.put(key, tile);

}

if (!tile.userHandle.contains(user)) {

tile.userHandle.add(user);

}

if (!outTiles.contains(tile)) {

outTiles.add(tile);

}

}

}

使非设置的应用中添加设置顶级菜单,需要添加一下activity的相关的属性,下面例子可以参考一下:

android:name="Activity"

android:label="@string/app_name"

android:name="com.android.settings.category"

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

android:name="com.android.settings.icon"

android:resource="@drawable/ic_launcher_setting" />

到了这里,数据加载流程基本上就总结完了,下面就要开始进行界面的加载了,要回到之前提到过的DashboardSummary的onCategoriesChanged()方法了.

(b)一级菜单界面加载,此处就比较简单了,是对View的绑定,我只把代码逻辑从头到位贴一下了   >.<

/src/com/android/settings/dashboard/DashboardSummary.java

@Override

public void onCategoriesChanged() {

// Bypass rebuildUI() on the first call of onCategoriesChanged, since rebuildUI() happens

// in onViewCreated as well when app starts. But, on the subsequent calls we need to

// rebuildUI() because there might be some changes to suggestions and categories.

if (isOnCategoriesChangedCalled) {

rebuildUI();

}

isOnCategoriesChangedCalled = true;

}

DashboardSummary.rebuildUI

@VisibleForTesting

void rebuildUI() {

if (!mSuggestionFeatureProvider.isSuggestionEnabled(getContext())) {

Log.d(TAG, "Suggestion feature is disabled, skipping suggestion entirely");

updateCategoryAndSuggestion(null /* tiles */);

} else {

new SuggestionLoader().execute();

// Set categories on their own if loading suggestions takes too long.

mHandler.postDelayed(() -> {

updateCategoryAndSuggestion(null /* tiles */);

}, MAX_WAIT_MILLIS);

}

}

DashboardSummary.updateCategoryAndSuggestion

@VisibleForTesting

void updateCategoryAndSuggestion(Listsuggestions) {

final Activity activity = getActivity();

if (activity == null) {

return;

}

final DashboardCategory category = mDashboardFeatureProvider.getTilesForCategory(

CategoryKey.CATEGORY_HOMEPAGE);

mSummaryLoader.updateSummaryToCache(category);

if (suggestions != null) {

mAdapter.setCategoriesAndSuggestions(category, suggestions);

} else {

mAdapter.setCategory(category);

}

}

./src/com/android/settings/dashboard/DashboardAdapter.java

public void setCategoriesAndSuggestions(DashboardCategory category,

Listsuggestions) {

tintIcons(category, suggestions);

final DashboardData prevData = mDashboardData;

mDashboardData = new DashboardData.Builder(prevData)

.setSuggestions(suggestions.subList(0,

Math.min(suggestions.size(), MAX_SUGGESTION_TO_SHOW)))

.setCategory(category)

.build();

notifyDashboardDataChanged(prevData);

ListshownSuggestions = null;

final int mode = mDashboardData.getSuggestionConditionMode();

if (mode == DashboardData.HEADER_MODE_DEFAULT) {

shownSuggestions = suggestions.subList(0,

Math.min(suggestions.size(), DashboardData.DEFAULT_SUGGESTION_COUNT));

} else if (mode != DashboardData.HEADER_MODE_COLLAPSED) {

shownSuggestions = suggestions;

}

if (shownSuggestions != null) {

for (Tile suggestion : shownSuggestions) {

final String identifier = mSuggestionFeatureProvider.getSuggestionIdentifier(

mContext, suggestion);

mMetricsFeatureProvider.action(

mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, identifier,

getSuggestionTaggedData());

mSuggestionsShownLogged.add(identifier);

}

}

}

public void setCategory(DashboardCategory category) {

tintIcons(category, null);

final DashboardData prevData = mDashboardData;

Log.d(TAG, "adapter setCategory called");

mDashboardData = new DashboardData.Builder(prevData)

.setCategory(category)

.build();

notifyDashboardDataChanged(prevData);

}

@Override

public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {

final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);

if (viewType == R.layout.suggestion_condition_header) {

return new SuggestionAndConditionHeaderHolder(view);

}

if (viewType == R.layout.suggestion_condition_container) {

return new SuggestionAndConditionContainerHolder(view);

}

return new DashboardItemHolder(view);

}

@Override

public void onBindViewHolder(DashboardItemHolder holder, int position) {

final int type = mDashboardData.getItemTypeByPosition(position);

switch (type) {

case R.layout.dashboard_tile:

final Tile tile = (Tile) mDashboardData.getItemEntityByPosition(position);

onBindTile(holder, tile);

holder.itemView.setTag(tile);

holder.itemView.setOnClickListener(mTileClickListener);

break;

case R.layout.suggestion_condition_container:

onBindConditionAndSuggestion(

(SuggestionAndConditionContainerHolder) holder, position);

break;

case R.layout.suggestion_condition_header:

onBindSuggestionConditionHeader((SuggestionAndConditionHeaderHolder) holder,

(SuggestionConditionHeaderData)

mDashboardData.getItemEntityByPosition(position));

break;

case R.layout.suggestion_condition_footer:

holder.itemView.setOnClickListener(v -> {

mMetricsFeatureProvider.action(mContext,

MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);

DashboardData prevData = mDashboardData;

mDashboardData = new DashboardData.Builder(prevData).setSuggestionConditionMode(

DashboardData.HEADER_MODE_COLLAPSED).build();

notifyDashboardDataChanged(prevData);

mRecyclerView.scrollToPosition(SUGGESTION_CONDITION_HEADER_POSITION);

});

break;

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值