Android 7.0Settings加载主界面流程
来源:互联网 发布:java构造器使用实例 编辑:程序博客网 时间:2024/06/05 08:37
新人一枚,没有整机环境,有什么写的不对欢迎批评指正,万分感谢!
Settings主界面加载时序图(这里很多判断逻辑我省略掉了。更多的是想把加载主界面流程跑通。)
这张流程图将主fragment DashboardSummary启动,RecyclerView数据加载刷新 显示得较为明白。但是对于主界面tile分类,tile排序,tile对象属性是无法得知的。所以接下我就主要讲讲这两个。
(1)先看一下主界面布局(用绘图画的,比较丑见谅)
这里有一个值得学习的是RecycleView分类加载不同的布局,值得学习。方法在DashboardAdapter.Java里面。
主界面对象介绍:
(1)主界面(除了Suggestion,condition)其他对象都在List<DashBoardCagtory> Categories里面
(2) Categories 有 4个对象。4个DashBoardCagtory的title值分别是 Wireless&networks,Device,Personal,System
(3)各个Cagtories都有List<Tile> tiles 是各个DashBoardCagtorytitle下面各个tile的集合
组装这些对象是时序图中方法4,5,6,7,8。
5 getTilesForAction
(1)组装Intent对象
(2)调用方法6
private static void getTilesForAction(Context context, UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache, String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings) { Intent intent = new Intent(action); if (requireSettings) { intent.setPackage(SETTING_PKG); } getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles, requireSettings, true); }
6 getTilesForIntent
(1)利用PM查询所有含有方法5生成的intent对象的ResolveInfo集合
(2)获取集合中每一个Acitvity manifest配置的meta标签name为SETTINGS_ACTION的value值
(3)把activity name 和package 生成Intent对象
(4)将2步骤的值赋值给Tile category
(5)获取action为SETTINGS_ACTION的intent-filter的priority属性
(6)将解析meta-data标签的bundle数据赋值给Tile metaData
public static void getTilesForIntent(Context context, UserHandle user, Intent intent, Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles, boolean usePriority, boolean checkCategory) { PackageManager pm = context.getPackageManager(); List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent, PackageManager.GET_META_DATA, user.getIdentifier()); for (ResolveInfo resolved : results) { if (!resolved.system) { continue; } ActivityInfo activityInfo = resolved.activityInfo; Bundle metaData = activityInfo.metaData; String categoryKey = defaultCategory; if (checkCategory && ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY)) && categoryKey == null) { continue; } else { categoryKey = metaData.getString(EXTRA_CATEGORY_KEY); } Pair<String, String> key = new Pair<String, String>(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; 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); } } }
7 updateTileData
(1) 解析meta-data标签 获取name为 META_DATA_PREFERENCE_ICON的value值赋值给icon
(2)解析meta-data标签 获取name为 META_DATA_PREFERENCE_TITLE的resource值赋值给title
(3)解析meta-data标签 获取name为 META_DATA_PREFERENCE_SUMMARY的value值赋值给summary
(4)如果title为空就获取acitvity标签label属性赋值给title
(5)如果icon等于0就获取acitvity标签icon属性赋值给icon
private static boolean updateTileData(Context context, Tile tile, ActivityInfo activityInfo, ApplicationInfo applicationInfo, PackageManager pm) { if (applicationInfo.isSystemApp()) { int icon = 0; CharSequence title = null; String summary = null; try { Resources res = pm.getResourcesForApplication( applicationInfo.packageName); Bundle metaData = activityInfo.metaData; if (res != null && metaData != null) { if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) { icon = metaData.getInt(META_DATA_PREFERENCE_ICON); } if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) { if (metaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) { title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE)); } else { title = metaData.getString(META_DATA_PREFERENCE_TITLE); } } if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) { if (metaData.get(META_DATA_PREFERENCE_SUMMARY) instanceof Integer) { summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY)); } else { summary = metaData.getString(META_DATA_PREFERENCE_SUMMARY); } } } } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { if (DEBUG) Log.d(LOG_TAG, "Couldn't find info", e); } if (TextUtils.isEmpty(title)) { title = activityInfo.loadLabel(pm).toString(); } if (icon == 0) { icon = activityInfo.icon; } tile.icon = Icon.createWithResource(activityInfo.packageName, icon); tile.title = title; tile.summary = summary; tile.intent = new Intent().setClassName(activityInfo.packageName, activityInfo.name); return true; } return false; }
8 createCategory
(1)创建DashboardCategory对象
(2)利用PM查询所有含有Tile对象categoriyKey生成的intent对象的ResolveInfo集合
(3)把acitivity label值赋值给category title属性
(4)把解析intent-filter标签的priority值赋值给category属性
private static DashboardCategory createCategory(Context context, String categoryKey) { DashboardCategory category = new DashboardCategory(); category.key = categoryKey; PackageManager pm = context.getPackageManager(); List<ResolveInfo> results = pm.queryIntentActivities(new Intent(categoryKey), 0); if (results.size() == 0) { return null; } for (ResolveInfo resolved : results) { if (!resolved.system) { // Do not allow any app to add to settings, only system ones. continue; } category.title = resolved.activityInfo.loadLabel(pm); category.priority = SETTING_PKG.equals( resolved.activityInfo.applicationInfo.packageName) ? resolved.priority : 0; if (DEBUG) Log.d(LOG_TAG, "Adding category " + category.title); } return category; }
4 getCategories
(1)调用5
(2)新建categoryMap集合
(3)遍历tiles,判断集合中是否有元素含有tile.category(当时我看见这个觉得google多此一举每次这个对象都是需要new但是我后来整 理manifest发现 含有相同category的tile很多。可以看我下面整理的manifest的表格)。如果没有就执行方法8
(4)将拥有相同属性category的tile加入到对象DashboardCategory category对象的list<tile>集合中(我觉得google写这个自己也蒙 蔽了。两个相同名字, 一个是Tile里面变量,一个是DashboardCategory对象)
(5)将categoryMap的值赋值给List <DashboardCategory>cagtories以便执行排序算法
(6)遍历cagtories利用Collections函数和比较器TILE_COMPARATOR将category.tiles按照priority从大到小排序
(7)利用Collections函数和比较器CATEGORY_COMPARATOR将categories按照priority从大到小排序
public static List<DashboardCategory> getCategories(Context context, HashMap<Pair<String, String>, Tile> cache) { final long startTime = System.currentTimeMillis(); boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) != 0; ArrayList<Tile> tiles = new ArrayList<>(); UserManager userManager = UserManager.get(context); 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); getTilesForAction(context, user, OPERATOR_SETTINGS, cache, OPERATOR_DEFAULT_CATEGORY, tiles, false); getTilesForAction(context, user, MANUFACTURER_SETTINGS, cache, MANUFACTURER_DEFAULT_CATEGORY, tiles, false); } if (setup) { getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false); } } HashMap<String, DashboardCategory> categoryMap = new HashMap<>(); for (Tile tile : tiles) { DashboardCategory category = categoryMap.get(tile.category); if (category == null) { category = createCategory(context, tile.category); if (category == null) { Log.w(LOG_TAG, "Couldn't find category " + tile.category); continue; } categoryMap.put(category.key, category); } category.addTile(tile); } ArrayList<DashboardCategory> categories = 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; }
图表:tile
图表:DashboardCategory
问题:summary是无法从manifest文件里面获取的。因为不含有meta-data name属性为“com.android.settings.summary”的标签。summary获取方法是时序图中方法16,17,18,31,32
16 new SummaryLoader
(1) 创建Handler对象public SummaryLoader(Activity activity, List<DashboardCategory> categories) { mHandler = new Handler(); mWorkerThread = new HandlerThread("SummaryLoader", Process.THREAD_PRIORITY_BACKGROUND); mWorkerThread.start(); mWorker = new Worker(mWorkerThread.getLooper()); mActivity = activity; for (int i = 0; i < categories.size(); i++) { List<Tile> tiles = categories.get(i).tiles; for (int j = 0; j < tiles.size(); j++) { Tile tile = tiles.get(j); mWorker.obtainMessage(Worker.MSG_GET_PROVIDER, tile).sendToTarget(); } } }
17 makeProviderW
(2)将方法18返回的SummaryProvider对象存入到mSummaryMap中
private synchronized void makeProviderW(Tile tile) { SummaryProvider provider = getSummaryProvider(tile); if (provider != null) { if (DEBUG) Log.d(TAG, "Creating " + tile); mSummaryMap.put(provider, tile.intent.getComponent()); } }
18 getSummaryProvider(真的是厉害了,我的歌)
(1)获取title中的meataData变量
(2)将bundle数据metaData键为SettingsActivity.META_DATA_KEY_FRAGMENT的alue值赋值给clsName
(3)根据clsName反射创建class
(4)获取name为SUMMARY_PROVIDER_FACORY的变量Field
(5)拿到Field的值强制转换为SummaryProviderFactory对象
(6)返回反射类中SummaryProvider对象
private SummaryProvider getSummaryProvider(Tile tile) { if (!mActivity.getPackageName().equals(tile.intent.getComponent().getPackageName())) { // Not within Settings, can't load Summary directly. // TODO: Load summary indirectly. return null; } Bundle metaData = getMetaData(tile); if (metaData == null) { if (DEBUG) Log.d(TAG, "No metadata specified for " + tile.intent.getComponent()); return null; } String clsName = metaData.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS); if (clsName == null) { if (DEBUG) Log.d(TAG, "No fragment specified for " + tile.intent.getComponent()); return null; } try { Class<?> cls = Class.forName(clsName); Field field = cls.getField(SUMMARY_PROVIDER_FACTORY); SummaryProviderFactory factory = (SummaryProviderFactory) field.get(null); return factory.createSummaryProvider(mActivity, this); } catch (ClassNotFoundException e) { if (DEBUG) Log.d(TAG, "Couldn't find " + clsName, e); } catch (NoSuchFieldException e) { if (DEBUG) Log.d(TAG, "Couldn't find " + SUMMARY_PROVIDER_FACTORY, e); } catch (ClassCastException e) { if (DEBUG) Log.d(TAG, "Couldn't cast " + SUMMARY_PROVIDER_FACTORY, e); } catch (IllegalAccessException e) { if (DEBUG) Log.d(TAG, "Couldn't get " + SUMMARY_PROVIDER_FACTORY, e); } return null; }
以NotificationApps.java为例
public class NotificationApps extends ManageApplications { private static class SummaryProvider implements SummaryLoader.SummaryProvider { private final Context mContext; private final SummaryLoader mLoader; private final NotificationBackend mNotificationBackend; private SummaryProvider(Context context, SummaryLoader loader) { mContext = context; mLoader = loader; mNotificationBackend = new NotificationBackend(); } @Override public void setListening(boolean listening) { if (listening) { new AppCounter(mContext) { @Override protected void onCountComplete(int num) { updateSummary(num); } @Override protected boolean includeInCount(ApplicationInfo info) { return mNotificationBackend.getNotificationsBanned(info.packageName, info.uid); } }.execute(); } } private void updateSummary(int count) { if (count == 0) { mLoader.setSummary(this, mContext.getString(R.string.notification_summary_none)); } else { mLoader.setSummary(this, mContext.getResources().getQuantityString( R.plurals.notification_summary, count, count)); } } } public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY = new SummaryLoader.SummaryProviderFactory() { @Override public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity, SummaryLoader summaryLoader) { return new SummaryProvider(activity, summaryLoader); } };}
31 setListening
public void setListening(boolean listening) { if (mListening == listening) return; mListening = listening; // Unregister listeners immediately. for (int i = 0; i < mReceivers.size(); i++) { mActivity.unregisterReceiver(mReceivers.valueAt(i)); } mReceivers.clear(); mWorker.removeMessages(Worker.MSG_SET_LISTENING); mWorker.obtainMessage(Worker.MSG_SET_LISTENING, listening ? 1 : 0, 0).sendToTarget(); }
32 setListeningW(注意锁)
(2)将反射fragment类中静态内部类执行setListening方法
private synchronized void setListeningW(boolean listening) { if (mWorkerListening == listening) return; mWorkerListening = listening; if (DEBUG) Log.d(TAG, "Listening " + listening); for (SummaryProvider p : mSummaryMap.keySet()) { try { p.setListening(listening); } catch (Exception e) { Log.d(TAG, "Problem in setListening", e); } } }到此界面第一次刷新完成。Suggestion这个类我还没研究透彻。所以就没有画到时序图里面。主要类是SuggestionParser.java
再介绍一下Settings.java的作用
正常我们写很多activity。是不是该这样写:
for(int i = 1 ; i <=需要新建activity的个数;i++) { 新建class Activityi extends SettingsActivity}
这样做没有什么不对,也不会对性能有影响。但是SettingsActivity真的是爸爸,儿子们需要做的都被爸爸干完了,所以你会发现许多空白文件。
Google就利用java静态内部类的机制(可以去百度一下)编译就会生成他的宿主类名$静态内部类名 也就是我们manifest看见类似于Settings$WirelessSettings。这样写就避免大量白痴文件。
Google 这次7.0Settings加载逻辑大改其实对于新界面开发更容易了。只要去manifest将你的activity属性配置好。Settings.java声明。目前代码逻辑就能直接把你界面加载完毕。然后你去对应的fragment实现逻辑。这样新界面开发基本不需要改动现有逻辑。真的是厉害了,我的歌!
- Android 7.0Settings加载主界面流程
- Android 7.0Settings加载主界面流程
- android 7.0 Settings列表加载流程
- 代码流程分析二:Settings-蓝牙分析-加载界面
- Android 7.0 Settings 加载选项
- 高通android 7.0短信会话界面加载流程
- Android5.1 原生应用设置(Settings)主界面启动流程
- Android蓝牙Settings之界面
- Android蓝牙Settings之界面
- Android蓝牙Settings之界面
- Android Settings源码流程分析
- androoid framework学习之Settings的主界面的代码流程分析
- android settings界面修改(添加等)
- Android L Settings界面结构简单分析
- Android M中Settings的启动流程
- Android 系统 Settings 启动流程详解
- Android软件主界面的设计流程
- Android 界面绘制流程
- HttpClient在HTTP协议接口测试中的使用
- 性能测试之----瓶颈分析方法
- Ant_build.xml的最完整解释
- LoadRunner 实现监控Tomcat
- linux awk命令详解
- Android 7.0Settings加载主界面流程
- 支付宝沙箱环境使用
- 登录验证码实现
- filter 拦截ajax请求
- Kerberos简介
- 解决Navicat 出错:1130-host . is not allowed to connect to this MySql server
- Linux tree命令
- 使用Tomcat j_security_check实现用户登录、注销功能
- ajax在spring环境下,从前端jsp页面传送页面提交值到后端controller