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;

}

}

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

  1. JDK8版本JAVA运行错误:找不到或无法加载主类 HelloJava的原因及解决方案

    JDK8版本JAVA运行错误:找不到或无法加载主类 HelloJava的原因及解决方案 一.错误描述 二.编写HelloJava代码 三.在命令行CMD窗口中运行代码 四.错误原因分析 解决方案 注: ...

  2. python动态加载模块有什么用_人生苦短我用python(02)动态加载模块

    继第一期[人生苦短我用Python系列专栏]发布后,深受广大睿普迷的一致好评,经常问小普第二期什么时候出呀?好期待~ 来来来小普这就呈上第二期[02动态加载模块] 错过了第一期的小伙伴们可以点此穿越哦 ...

  3. android系统设置在哪里,android-如何在系统settings里添加设置选项

    版本:2.3.1 目的:在通话设置菜单下,添加一dect设置菜单,里面再添加一checkBOxPreference 来使能硬件模块. ------------------------- 目前做的项目, ...

  4. android setting.java,Android Setting 启动流程总结

    总结: 首先,找到Settings目录,打开AndroidManifest文件,确定Setting启动时调用的类Settings.java.Settings继承于SettingsActivity,内部 ...

  5. 【Android 逆向】Android 进程注入工具开发 ( 远程进程注入动态库文件操作 | 注入动态库 加载 业务动态库 | 业务动态库启动 | pthread_create 线程开发 )

    文章目录 前言 一.加载 libnattive.so 动态库 二. libnattive.so 动态库启动 三. pthread_create 线程开发 四. 线程执行函数 前言 libbridge. ...

  6. Android性能优化之解密ZAKER,网易云阅读等新闻应用的内容缓存加载方式

    我是比较关注时事的, 每天都会花一点事件去看看新闻什么的. 因此类似ZAKER, 网易云阅读等这类的资讯聚合类应用是我的钟爱, 并且这些应用也确实做得很好,值得学习! 前面一篇文章, 讲了缓存的一些构 ...

  7. android欢迎界面动画加载

    欢迎界面 WelcomeActivity .java public class WelcomeActivity extends Activity implements AnimationListene ...

  8. android 官方上拉,手把手教你实现RecyclerView的下拉刷新和上拉加载更多

    纵观多数App,下拉刷新和上拉加载更多是很常见的功能,但是谷歌官方只有一个SwipeRefreshLayout用来下拉刷新,上拉加载更多还要自己做. 基于RecyclerView简单封装了这两个操作, ...

  9. 对android小程序的结论,微信小程序引入外部字体总结(针对安卓加载缓慢问题)...

    最近有个项目需求,需要改变小程序所有文字的字体. 查了资料后发现,本地加载字体文件导致小程序太大.动态加载文件,苹果真机完美,但是在安卓的真机上引入的外部字体加载会很慢,会有很明显的默认字体切换到外部 ...

最新文章

  1. Strategy模式
  2. 分享.Net 设计模式大全
  3. Hankson的趣味题 解题记录
  4. [CTSC2018]混合果汁
  5. 半径对氢原子基态能级的影响H
  6. 计算机系统基础:计算机可靠性知识笔记
  7. wget抓取网站, 模拟手机端抓取
  8. Exchange日志清理
  9. html有3d效果的网页,HTML5如何在网页中实现3D效果?
  10. 8.4 Change Reference to Value(将引用对象改为值对象)
  11. 试着当个“刺头”,不要被客户“牵着”鼻子走
  12. termux python教程_Termux 入门教程:架设手机 Server 下载文件
  13. fme坐标转换器_FME坐标点提取
  14. 单表(sqlserver不支持)、整库,支持本地和远程备份
  15. python输入水果查询个数_Python练习题4.9查询水果价格
  16. 淘宝店铺装修代码大全
  17. 程序员常用的网站合集
  18. app应用分发平台|苹果ios超级签名|APP封装打包|应用内测托管平台|iOS应用企业签名|Android应用上传内测-虾分发
  19. 人脸识别机与服务器访问协议,人脸识别终端485通信协议.doc
  20. 那些人尽可夫的男人啊——黄金圣斗士对同人女的真情告白2

热门文章

  1. Git客户端TortoiseGit(Windows系统)的使用方法
  2. 【C#】【APK】APK文件解析AXML-层层深入APK文件解析之一
  3. 如何在页面调用JS函数的代码
  4. golang 字节切片 数组 字符串 互转
  5. linux 挂载错误 mount: unknown filesystem type LVM2_member 解决方法
  6. linux c 内存泄漏调试工具 《valgrind用户手册》 2. 使用和理解Valgrind核心
  7. web前端 vue、react、angular三大框架对比 与jquery的对比
  8. cve -2016-6663 mysql 本地提权
  9. golang RSA (PKCS#1)加密解密
  10. 413 Request Entity Too Large 的解决方法