android setting模块,android O版本 设置(Settings)模块总结--设置的一级界面的加载
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)模块总结--设置的一级界面的加载相关推荐
- JDK8版本JAVA运行错误:找不到或无法加载主类 HelloJava的原因及解决方案
JDK8版本JAVA运行错误:找不到或无法加载主类 HelloJava的原因及解决方案 一.错误描述 二.编写HelloJava代码 三.在命令行CMD窗口中运行代码 四.错误原因分析 解决方案 注: ...
- python动态加载模块有什么用_人生苦短我用python(02)动态加载模块
继第一期[人生苦短我用Python系列专栏]发布后,深受广大睿普迷的一致好评,经常问小普第二期什么时候出呀?好期待~ 来来来小普这就呈上第二期[02动态加载模块] 错过了第一期的小伙伴们可以点此穿越哦 ...
- android系统设置在哪里,android-如何在系统settings里添加设置选项
版本:2.3.1 目的:在通话设置菜单下,添加一dect设置菜单,里面再添加一checkBOxPreference 来使能硬件模块. ------------------------- 目前做的项目, ...
- android setting.java,Android Setting 启动流程总结
总结: 首先,找到Settings目录,打开AndroidManifest文件,确定Setting启动时调用的类Settings.java.Settings继承于SettingsActivity,内部 ...
- 【Android 逆向】Android 进程注入工具开发 ( 远程进程注入动态库文件操作 | 注入动态库 加载 业务动态库 | 业务动态库启动 | pthread_create 线程开发 )
文章目录 前言 一.加载 libnattive.so 动态库 二. libnattive.so 动态库启动 三. pthread_create 线程开发 四. 线程执行函数 前言 libbridge. ...
- Android性能优化之解密ZAKER,网易云阅读等新闻应用的内容缓存加载方式
我是比较关注时事的, 每天都会花一点事件去看看新闻什么的. 因此类似ZAKER, 网易云阅读等这类的资讯聚合类应用是我的钟爱, 并且这些应用也确实做得很好,值得学习! 前面一篇文章, 讲了缓存的一些构 ...
- android欢迎界面动画加载
欢迎界面 WelcomeActivity .java public class WelcomeActivity extends Activity implements AnimationListene ...
- android 官方上拉,手把手教你实现RecyclerView的下拉刷新和上拉加载更多
纵观多数App,下拉刷新和上拉加载更多是很常见的功能,但是谷歌官方只有一个SwipeRefreshLayout用来下拉刷新,上拉加载更多还要自己做. 基于RecyclerView简单封装了这两个操作, ...
- 对android小程序的结论,微信小程序引入外部字体总结(针对安卓加载缓慢问题)...
最近有个项目需求,需要改变小程序所有文字的字体. 查了资料后发现,本地加载字体文件导致小程序太大.动态加载文件,苹果真机完美,但是在安卓的真机上引入的外部字体加载会很慢,会有很明显的默认字体切换到外部 ...
最新文章
- Strategy模式
- 分享.Net 设计模式大全
- Hankson的趣味题 解题记录
- [CTSC2018]混合果汁
- 半径对氢原子基态能级的影响H
- 计算机系统基础:计算机可靠性知识笔记
- wget抓取网站, 模拟手机端抓取
- Exchange日志清理
- html有3d效果的网页,HTML5如何在网页中实现3D效果?
- 8.4 Change Reference to Value(将引用对象改为值对象)
- 试着当个“刺头”,不要被客户“牵着”鼻子走
- termux python教程_Termux 入门教程:架设手机 Server 下载文件
- fme坐标转换器_FME坐标点提取
- 单表(sqlserver不支持)、整库,支持本地和远程备份
- python输入水果查询个数_Python练习题4.9查询水果价格
- 淘宝店铺装修代码大全
- 程序员常用的网站合集
- app应用分发平台|苹果ios超级签名|APP封装打包|应用内测托管平台|iOS应用企业签名|Android应用上传内测-虾分发
- 人脸识别机与服务器访问协议,人脸识别终端485通信协议.doc
- 那些人尽可夫的男人啊——黄金圣斗士对同人女的真情告白2
热门文章
- Git客户端TortoiseGit(Windows系统)的使用方法
- 【C#】【APK】APK文件解析AXML-层层深入APK文件解析之一
- 如何在页面调用JS函数的代码
- golang 字节切片 数组 字符串 互转
- linux 挂载错误 mount: unknown filesystem type LVM2_member 解决方法
- linux c 内存泄漏调试工具 《valgrind用户手册》 2. 使用和理解Valgrind核心
- web前端 vue、react、angular三大框架对比 与jquery的对比
- cve -2016-6663 mysql 本地提权
- golang RSA (PKCS#1)加密解密
- 413 Request Entity Too Large 的解决方法