当前位置:   article > 正文

android O版本 设置(Settings)模块总结--设置的一级界面的加载_com.android.settings.action.extra_settings

com.android.settings.action.extra_settings

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

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

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

    在上一篇 <<android O版本 设置(Settings)模块总结--设置的启动界面选择>> 中有说到DashboardSummary是顶级菜单的容器,那么数据的获取和它也就有关系了:

    DashboardSummary.java

  1. @Override
  2. public void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. final Activity activity = getActivity();
  5.         mDashboardFeatureProvider = FeatureFactory.getFactory(activity)
  6. .getDashboardFeatureProvider(activity);
  7. mSuggestionFeatureProvider = FeatureFactory.getFactory(activity)
  8. .getSuggestionFeatureProvider(activity);
  9. mSummaryLoader = new SummaryLoader(activity, CategoryKey.CATEGORY_HOMEPAGE);
  10. mConditionManager = ConditionManager.get(activity, false);
  11. getLifecycle().addObserver(mConditionManager);
  12. mSuggestionParser = new SuggestionParser(activity,
  13. activity.getSharedPreferences(SUGGESTIONS, 0), R.xml.suggestion_ordering);
  14. mSuggestionsChecks = new SuggestionsChecks(getContext());
  15. }

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

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

  1. public DashboardFeatureProviderImpl(Context context) {
  2. mContext = context.getApplicationContext();
  3. mCategoryManager = CategoryManager.get(context, getExtraIntentAction());
  4. mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
  5. mPackageManager = context.getPackageManager();
  6. }
  7. @Override
  8. public DashboardCategory getTilesForCategory(String key) {
  9. return mCategoryManager.getTilesByCategory(mContext, key);
  10. }

 

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

 

  1. public static CategoryManager get(Context context, String action) {
  2. if (sInstance == null) {
  3. sInstance = new CategoryManager(context, action);
  4. }
  5. return sInstance;
  6. }
  7. CategoryManager(Context context, String action) {
  8. mTileByComponentCache = new ArrayMap<>();
  9. mCategoryByKeyMap = new ArrayMap<>();
  10. mInterestingConfigChanges = new InterestingConfigChanges();
  11. mInterestingConfigChanges.applyNewConfig(context.getResources());
  12. mExtraAction = action;
  13. }

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

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

  1. SettingsDrawerActivity.java
  2. @Override
  3. protected void onResume() {
  4. super.onResume();
  5. final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
  6. filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
  7. filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
  8. filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
  9. filter.addDataScheme("package");
  10. registerReceiver(mPackageReceiver, filter);
  11. new CategoriesUpdateTask().execute();
  12. final Intent intent = getIntent();
  13. if (intent != null && intent.getBooleanExtra(EXTRA_SHOW_MENU, false)) {
  14. // Intent explicitly set to show menu.
  15. showMenuIcon();
  16. }
  17. }

 

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

 

new CategoriesUpdateTask().execute();

  1. private class CategoriesUpdateTask extends AsyncTask<Void, Void, Void> {
  2. private final CategoryManager mCategoryManager;
  3. public CategoriesUpdateTask() {
  4. mCategoryManager = CategoryManager.get(SettingsDrawerActivity.this);
  5. }
  6. @Override
  7. protected Void doInBackground(Void... params) {
  8. mCategoryManager.reloadAllCategories(SettingsDrawerActivity.this, getSettingPkg());
  9. return null;
  10. }
  11. @Override
  12. protected void onPostExecute(Void result) {
  13. mCategoryManager.updateCategoryFromBlacklist(sTileBlacklist);
  14. onCategoriesChanged();
  15. }
  16. }

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

 

  1.     DashboardSummary
  2.      @Override
  3. public void onCategoriesChanged() {
  4. // Bypass rebuildUI() on the first call of onCategoriesChanged, since rebuildUI() happens
  5. // in onViewCreated as well when app starts. But, on the subsequent calls we need to
  6. // rebuildUI() because there might be some changes to suggestions and categories.
  7. if (isOnCategoriesChangedCalled) {
  8. rebuildUI();
  9. }
  10. isOnCategoriesChangedCalled = true;
  11. }

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

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

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

  1. private synchronized void tryInitCategories(Context context, boolean forceClearCache,
  2. String settingPkg) {
  3. if (mCategories == null) {
  4. if (forceClearCache) {
  5. mTileByComponentCache.clear();
  6. }
  7. //清除缓存
  8. mCategoryByKeyMap.clear();
  9. //数据获取
  10. mCategories = TileUtils.getCategories(context, mTileByComponentCache,
  11. false /* categoryDefinedInManifest */, mExtraAction, settingPkg);
  12. //数据保存
  13. for (DashboardCategory category : mCategories) {
  14. mCategoryByKeyMap.put(category.key, category);
  15. }
  16. backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
  17. //数据排序清理重复等
  18. sortCategories(context, mCategoryByKeyMap);
  19. filterDuplicateTiles(mCategoryByKeyMap);
  20. }
  21. }
 

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

  1. public static List<DashboardCategory> getCategories(Context context,
  2. Map<Pair<String, String>, Tile> cache, boolean categoryDefinedInManifest,
  3. String extraAction, String settingPkg) {
  4. final long startTime = System.currentTimeMillis();
  5. boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0)
  6. != 0;
  7. ArrayList<Tile> tiles = new ArrayList<>();
  8. UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
  9. //此处是整个数据的获取
  10. for (UserHandle user : userManager.getUserProfiles()) {
  11. // TODO: Needs much optimization, too many PM queries going on here.
  12. if (user.getIdentifier() == ActivityManager.getCurrentUser()) {
  13. // Only add Settings for this user.
  14. getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true,
  15. settingPkg);
  16. getTilesForAction(context, user, OPERATOR_SETTINGS, cache,
  17. OPERATOR_DEFAULT_CATEGORY, tiles, false, true, settingPkg);
  18. getTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,
  19. MANUFACTURER_DEFAULT_CATEGORY, tiles, false, true, settingPkg);
  20. }
  21. if (setup) {
  22. getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false,
  23. settingPkg);
  24. if (!categoryDefinedInManifest) {
  25. getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false,
  26. settingPkg);
  27. if (extraAction != null) {
  28. getTilesForAction(context, user, extraAction, cache, null, tiles, false,
  29. settingPkg);
  30. }
  31. }
  32. }
  33. }
  34. //按照进行meta-data android:name="com.android.settings.category"进行分类
  35. HashMap<String, DashboardCategory> categoryMap = new HashMap<>();
  36. for (Tile tile : tiles) {
  37. DashboardCategory category = categoryMap.get(tile.category);
  38. if (category == null) {
  39. category = createCategory(context, tile.category, categoryDefinedInManifest);
  40. if (category == null) {
  41. Log.w(LOG_TAG, "Couldn't find category " + tile.category);
  42. continue;
  43. }
  44. categoryMap.put(category.key, category);
  45. }
  46. category.addTile(tile);
  47. }
  48. //对分类进行排序
  49. ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());
  50. for (DashboardCategory category : categories) {
  51. Collections.sort(category.tiles, TILE_COMPARATOR);
  52. }
  53. Collections.sort(categories, CATEGORY_COMPARATOR);
  54. if (DEBUG_TIMING) Log.d(LOG_TAG, "getCategories took "
  55. + (System.currentTimeMillis() - startTime) + " ms");
  56. return categories;
  57. }


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

 

  1. private static final String SETTINGS_ACTION =
  2. "com.android.settings.action.SETTINGS";
  3. private static final String OPERATOR_SETTINGS =
  4. "com.android.settings.OPERATOR_APPLICATION_SETTING";
  5. private static final String MANUFACTURER_SETTINGS =
  6. "com.android.settings.MANUFACTURER_APPLICATION_SETTING";
  7. private static final String EXTRA_SETTINGS_ACTION =
  8. "com.android.settings.action.EXTRA_SETTINGS";
  9. private static void getTilesForAction(Context context,
  10. UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
  11. String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings,
  12. boolean usePriority, String settingPkg) {
  13. //填充Intent
  14. Intent intent = new Intent(action);
  15. if (requireSettings) {
  16. intent.setPackage(settingPkg);
  17. }
  18. getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles,
  19. usePriority, true);
  20. }
  21. public static void getTilesForIntent(Context context, UserHandle user, Intent intent,
  22. Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles,
  23. boolean usePriority, boolean checkCategory) {
  24. PackageManager pm = context.getPackageManager();
  25. //通过PM 在已经安装的应用中获取指定Action的Activity信息.
  26. List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
  27. PackageManager.GET_META_DATA, user.getIdentifier());
  28. for (ResolveInfo resolved : results) {
  29. //此处是对非系统应用添加设置菜单做了限制的
  30. if (!resolved.system) {
  31. // Do not allow any app to add to settings, only system ones.
  32. continue;
  33. }
  34. ActivityInfo activityInfo = resolved.activityInfo;
  35. Bundle metaData = activityInfo.metaData;
  36. String categoryKey = defaultCategory;
  37. //通过meta-data android:name="com.android.settings.category"获取菜单分类.
  38. //例如:com.android.settings.category.ia.homepage就是在一级目录
  39. //例如:com.android.settings.category.ia.device显示在顶级菜单"设备链接"中
  40. // Load category
  41. if (checkCategory && ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY))
  42. && categoryKey == null) {
  43. Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent "
  44. + intent + " missing metadata "
  45. + (metaData == null ? "" : EXTRA_CATEGORY_KEY));
  46. continue;
  47. } else {
  48. categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
  49. }
  50. Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
  51. activityInfo.name);
  52. Tile tile = addedCache.get(key);
  53. if (tile == null) {
  54. tile = new Tile();
  55. tile.intent = new Intent().setClassName(
  56. activityInfo.packageName, activityInfo.name);
  57. tile.category = categoryKey;
  58. tile.priority = usePriority ? resolved.priority : 0;
  59. tile.metaData = activityInfo.metaData;
  60. //菜单的标题,图标,概要(summary),对应Activity的获取和赋值,这个可以自行分析下
  61. updateTileData(context, tile, activityInfo, activityInfo.applicationInfo,
  62. pm);
  63. if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title);
  64. addedCache.put(key, tile);
  65. }
  66. if (!tile.userHandle.contains(user)) {
  67. tile.userHandle.add(user);
  68. }
  69. if (!outTiles.contains(tile)) {
  70. outTiles.add(tile);
  71. }
  72. }
  73. }

 

使非设置的应用中添加设置顶级菜单,需要添加一下activity的相关的属性,下面例子可以参考一下:
  <activity
                  android:name="Activity"
                  android:label="@string/app_name"
            <intent-filter android:priority="6">
                <action android:name="com.android.settings.action.EXTRA_SETTINGS" />
            </intent-filter>
            <meta-data
                android:name="com.android.settings.category"
                android:value="com.android.settings.category.ia.homepage" />
            <meta-data
                android:name="com.android.settings.icon"
                android:resource="@drawable/ic_launcher_setting" />
        </activity>

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

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

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

  1. @Override
  2. public void onCategoriesChanged() {
  3. // Bypass rebuildUI() on the first call of onCategoriesChanged, since rebuildUI() happens
  4. // in onViewCreated as well when app starts. But, on the subsequent calls we need to
  5. // rebuildUI() because there might be some changes to suggestions and categories.
  6. if (isOnCategoriesChangedCalled) {
  7. rebuildUI();
  8. }
  9. isOnCategoriesChangedCalled = true;
  10. }

DashboardSummary.rebuildUI

  1. @VisibleForTesting
  2. void rebuildUI() {
  3. if (!mSuggestionFeatureProvider.isSuggestionEnabled(getContext())) {
  4. Log.d(TAG, "Suggestion feature is disabled, skipping suggestion entirely");
  5. updateCategoryAndSuggestion(null /* tiles */);
  6. } else {
  7. new SuggestionLoader().execute();
  8. // Set categories on their own if loading suggestions takes too long.
  9. mHandler.postDelayed(() -> {
  10. updateCategoryAndSuggestion(null /* tiles */);
  11. }, MAX_WAIT_MILLIS);
  12. }
  13. }

DashboardSummary.updateCategoryAndSuggestion

  1. @VisibleForTesting
  2. void updateCategoryAndSuggestion(List<Tile> suggestions) {
  3. final Activity activity = getActivity();
  4. if (activity == null) {
  5. return;
  6. }
  7. final DashboardCategory category = mDashboardFeatureProvider.getTilesForCategory(
  8. CategoryKey.CATEGORY_HOMEPAGE);
  9. mSummaryLoader.updateSummaryToCache(category);
  10. if (suggestions != null) {
  11. mAdapter.setCategoriesAndSuggestions(category, suggestions);
  12. } else {
  13. mAdapter.setCategory(category);
  14. }
  15. }

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

  1. public void setCategoriesAndSuggestions(DashboardCategory category,
  2. List<Tile> suggestions) {
  3. tintIcons(category, suggestions);
  4. final DashboardData prevData = mDashboardData;
  5. mDashboardData = new DashboardData.Builder(prevData)
  6. .setSuggestions(suggestions.subList(0,
  7. Math.min(suggestions.size(), MAX_SUGGESTION_TO_SHOW)))
  8. .setCategory(category)
  9. .build();
  10. notifyDashboardDataChanged(prevData);
  11. List<Tile> shownSuggestions = null;
  12. final int mode = mDashboardData.getSuggestionConditionMode();
  13. if (mode == DashboardData.HEADER_MODE_DEFAULT) {
  14. shownSuggestions = suggestions.subList(0,
  15. Math.min(suggestions.size(), DashboardData.DEFAULT_SUGGESTION_COUNT));
  16. } else if (mode != DashboardData.HEADER_MODE_COLLAPSED) {
  17. shownSuggestions = suggestions;
  18. }
  19. if (shownSuggestions != null) {
  20. for (Tile suggestion : shownSuggestions) {
  21. final String identifier = mSuggestionFeatureProvider.getSuggestionIdentifier(
  22. mContext, suggestion);
  23. mMetricsFeatureProvider.action(
  24. mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, identifier,
  25. getSuggestionTaggedData());
  26. mSuggestionsShownLogged.add(identifier);
  27. }
  28. }
  29. }
  30. public void setCategory(DashboardCategory category) {
  31. tintIcons(category, null);
  32. final DashboardData prevData = mDashboardData;
  33. Log.d(TAG, "adapter setCategory called");
  34. mDashboardData = new DashboardData.Builder(prevData)
  35. .setCategory(category)
  36. .build();
  37. notifyDashboardDataChanged(prevData);
  38. }
  1. @Override
  2. public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  3. final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
  4. if (viewType == R.layout.suggestion_condition_header) {
  5. return new SuggestionAndConditionHeaderHolder(view);
  6. }
  7. if (viewType == R.layout.suggestion_condition_container) {
  8. return new SuggestionAndConditionContainerHolder(view);
  9. }
  10. return new DashboardItemHolder(view);
  11. }
  12. @Override
  13. public void onBindViewHolder(DashboardItemHolder holder, int position) {
  14. final int type = mDashboardData.getItemTypeByPosition(position);
  15. switch (type) {
  16. case R.layout.dashboard_tile:
  17. final Tile tile = (Tile) mDashboardData.getItemEntityByPosition(position);
  18. onBindTile(holder, tile);
  19. holder.itemView.setTag(tile);
  20. holder.itemView.setOnClickListener(mTileClickListener);
  21. break;
  22. case R.layout.suggestion_condition_container:
  23. onBindConditionAndSuggestion(
  24. (SuggestionAndConditionContainerHolder) holder, position);
  25. break;
  26. case R.layout.suggestion_condition_header:
  27. onBindSuggestionConditionHeader((SuggestionAndConditionHeaderHolder) holder,
  28. (SuggestionConditionHeaderData)
  29. mDashboardData.getItemEntityByPosition(position));
  30. break;
  31. case R.layout.suggestion_condition_footer:
  32. holder.itemView.setOnClickListener(v -> {
  33. mMetricsFeatureProvider.action(mContext,
  34. MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
  35. DashboardData prevData = mDashboardData;
  36. mDashboardData = new DashboardData.Builder(prevData).setSuggestionConditionMode(
  37. DashboardData.HEADER_MODE_COLLAPSED).build();
  38. notifyDashboardDataChanged(prevData);
  39. mRecyclerView.scrollToPosition(SUGGESTION_CONDITION_HEADER_POSITION);
  40. });
  41. break;
  42. }
  43. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/458056
推荐阅读
相关标签
  

闽ICP备14008679号