Android 7.0Settings加载主界面流程

来源:互联网 发布:php招聘要求不靠谱 编辑:程序博客网 时间:2024/06/04 20:40

Android 7.0Settings加载主界面流程

标签: androidSourceCode界面
 1066人阅读 评论(1) 收藏 举报
 分类:
 

目录(?)[+]

新人一枚,没有整机环境,有什么写的不对欢迎批评指正,万分感谢!

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

[java] view plain copy
  1. private static void getTilesForAction(Context context,  
  2.         UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,  
  3.         String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings) {  
  4.     Intent intent = new Intent(action);  
  5.     if (requireSettings) {  
  6.         intent.setPackage(SETTING_PKG);  
  7.     }  
  8.     getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles,  
  9.             requireSettings, true);  
  10. }  


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

[java] view plain copy
  1. public static void getTilesForIntent(Context context, UserHandle user, Intent intent,  
  2.             Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles,  
  3.             boolean usePriority, boolean checkCategory) {  
  4.         PackageManager pm = context.getPackageManager();  
  5.         List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,  
  6.                 PackageManager.GET_META_DATA, user.getIdentifier());  
  7.         for (ResolveInfo resolved : results) {  
  8.             if (!resolved.system) {  
  9.                 continue;  
  10.             }  
  11.             ActivityInfo activityInfo = resolved.activityInfo;  
  12.             Bundle metaData = activityInfo.metaData;  
  13.             String categoryKey = defaultCategory;  
  14.             if (checkCategory && ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY))  
  15.                     && categoryKey == null) {  
  16.                 continue;  
  17.             } else {  
  18.                 categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);  
  19.             }  
  20.             Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,  
  21.                     activityInfo.name);  
  22.             Tile tile = addedCache.get(key);  
  23.             if (tile == null) {  
  24.                 tile = new Tile();  
  25.                 tile.intent = new Intent().setClassName(  
  26.                         activityInfo.packageName, activityInfo.name);  
  27.                 tile.category = categoryKey;  
  28.                 tile.priority = usePriority ? resolved.priority : 0;  
  29.                 tile.metaData = activityInfo.metaData;  
  30.                 updateTileData(context, tile, activityInfo, activityInfo.applicationInfo,  
  31.                         pm);  
  32.                 if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title);  
  33.   
  34.                 addedCache.put(key, tile);  
  35.             }  
  36.             if (!tile.userHandle.contains(user)) {  
  37.                 tile.userHandle.add(user);  
  38.             }  
  39.             if (!outTiles.contains(tile)) {  
  40.                 outTiles.add(tile);  
  41.             }  
  42.         }  
  43.     }  


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

[java] view plain copy
  1. private static boolean updateTileData(Context context, Tile tile,  
  2.           ActivityInfo activityInfo, ApplicationInfo applicationInfo, PackageManager pm) {  
  3.       if (applicationInfo.isSystemApp()) {  
  4.           int icon = 0;  
  5.           CharSequence title = null;  
  6.           String summary = null;  
  7.           try {  
  8.               Resources res = pm.getResourcesForApplication(  
  9.                       applicationInfo.packageName);  
  10.               Bundle metaData = activityInfo.metaData;  
  11.   
  12.               if (res != null && metaData != null) {  
  13.                   if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) {  
  14.                       icon = metaData.getInt(META_DATA_PREFERENCE_ICON);  
  15.                   }  
  16.                   if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {  
  17.                       if (metaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) {  
  18.                           title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));  
  19.                       } else {  
  20.                           title = metaData.getString(META_DATA_PREFERENCE_TITLE);  
  21.                       }  
  22.                   }  
  23.                   if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {  
  24.                       if (metaData.get(META_DATA_PREFERENCE_SUMMARY) instanceof Integer) {  
  25.                           summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY));  
  26.                       } else {  
  27.                           summary = metaData.getString(META_DATA_PREFERENCE_SUMMARY);  
  28.                       }  
  29.                   }  
  30.               }  
  31.           } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {  
  32.               if (DEBUG) Log.d(LOG_TAG, "Couldn't find info", e);  
  33.           }  
  34.           if (TextUtils.isEmpty(title)) {  
  35.               title = activityInfo.loadLabel(pm).toString();  
  36.           }  
  37.           if (icon == 0) {  
  38.               icon = activityInfo.icon;  
  39.           }  
  40.           tile.icon = Icon.createWithResource(activityInfo.packageName, icon);  
  41.           tile.title = title;  
  42.           tile.summary = summary;  
  43.           tile.intent = new Intent().setClassName(activityInfo.packageName,  
  44.                   activityInfo.name);  
  45.   
  46.           return true;  
  47.       }  
  48.   
  49.       return false;  
  50.   }  


8 createCategory

(1)创建DashboardCategory对象

(2)利用PM查询所有含有Tile对象categoriyKey生成的intent对象的ResolveInfo集合

(3)把acitivity label值赋值给category title属性

(4)把解析intent-filter标签的priority值赋值给category属性

[java] view plain copy
  1. private static DashboardCategory createCategory(Context context, String categoryKey) {  
  2.     DashboardCategory category = new DashboardCategory();  
  3.     category.key = categoryKey;  
  4.     PackageManager pm = context.getPackageManager();  
  5.     List<ResolveInfo> results = pm.queryIntentActivities(new Intent(categoryKey), 0);  
  6.     if (results.size() == 0) {  
  7.         return null;  
  8.     }  
  9.     for (ResolveInfo resolved : results) {  
  10.         if (!resolved.system) {  
  11.             // Do not allow any app to add to settings, only system ones.  
  12.             continue;  
  13.         }  
  14.         category.title = resolved.activityInfo.loadLabel(pm);  
  15.         category.priority = SETTING_PKG.equals(  
  16.                 resolved.activityInfo.applicationInfo.packageName) ? resolved.priority : 0;  
  17.         if (DEBUG) Log.d(LOG_TAG, "Adding category " + category.title);  
  18.     }  
  19.   
  20.     return category;  
  21. }  


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从大到小排序


[java] view plain copy
  1. public static List<DashboardCategory> getCategories(Context context,  
  2.             HashMap<Pair<String, String>, Tile> cache) {  
  3.         final long startTime = System.currentTimeMillis();  
  4.         boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0)  
  5.                 != 0;  
  6.         ArrayList<Tile> tiles = new ArrayList<>();  
  7.         UserManager userManager = UserManager.get(context);  
  8.         for (UserHandle user : userManager.getUserProfiles()) {  
  9.             // TODO: Needs much optimization, too many PM queries going on here.  
  10.             if (user.getIdentifier() == ActivityManager.getCurrentUser()) {  
  11.                 // Only add Settings for this user.  
  12.                 getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true);  
  13.                 getTilesForAction(context, user, OPERATOR_SETTINGS, cache,  
  14.                         OPERATOR_DEFAULT_CATEGORY, tiles, false);  
  15.                 getTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,  
  16.                         MANUFACTURER_DEFAULT_CATEGORY, tiles, false);  
  17.             }  
  18.             if (setup) {  
  19.                 getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false);  
  20.             }  
  21.         }  
  22.         HashMap<String, DashboardCategory> categoryMap = new HashMap<>();  
  23.         for (Tile tile : tiles) {  
  24.             DashboardCategory category = categoryMap.get(tile.category);  
  25.             if (category == null) {  
  26.                 category = createCategory(context, tile.category);  
  27.                 if (category == null) {  
  28.                     Log.w(LOG_TAG, "Couldn't find category " + tile.category);  
  29.                     continue;  
  30.                 }  
  31.                 categoryMap.put(category.key, category);  
  32.             }  
  33.             category.addTile(tile);  
  34.         }  
  35.         ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());  
  36.         for (DashboardCategory category : categories) {  
  37.             Collections.sort(category.tiles, TILE_COMPARATOR);  
  38.         }  
  39.         Collections.sort(categories, CATEGORY_COMPARATOR);  
  40.         if (DEBUG_TIMING) Log.d(LOG_TAG, "getCategories took "  
  41.                 + (System.currentTimeMillis() - startTime) + " ms");  
  42.         return categories;  
  43.     }  




图表:tile



图表:DashboardCategory


问题:summary是无法从manifest文件里面获取的。因为不含有meta-data name属性为“com.android.settings.summary”的标签。summary获取方法是时序图中方法16,17,18,31,32

16 new SummaryLoader

(1) 创建Handler对象
(2) 创建异步线程(大家可以去看一下线程优先级,做app还是挺有用处的)
(3) 遍历categories里面每一个DashboardCategory对象里面集合Tiles里面每一个tile对象
(4) 发送消息执行方法17

[java] view plain copy
  1. public SummaryLoader(Activity activity, List<DashboardCategory> categories) {  
  2.     mHandler = new Handler();  
  3.     mWorkerThread = new HandlerThread("SummaryLoader", Process.THREAD_PRIORITY_BACKGROUND);  
  4.     mWorkerThread.start();  
  5.     mWorker = new Worker(mWorkerThread.getLooper());  
  6.     mActivity = activity;  
  7.     for (int i = 0; i < categories.size(); i++) {  
  8.         List<Tile> tiles = categories.get(i).tiles;  
  9.         for (int j = 0; j < tiles.size(); j++) {  
  10.             Tile tile = tiles.get(j);  
  11.             mWorker.obtainMessage(Worker.MSG_GET_PROVIDER, tile).sendToTarget();  
  12.         }  
  13.     }  
  14. }  

17 makeProviderW

(1)执行方法18

(2)将方法18返回的SummaryProvider对象存入到mSummaryMap中

[java] view plain copy
  1. private synchronized void makeProviderW(Tile tile) {  
  2.     SummaryProvider provider = getSummaryProvider(tile);  
  3.     if (provider != null) {  
  4.         if (DEBUG) Log.d(TAG, "Creating " + tile);  
  5.         mSummaryMap.put(provider, tile.intent.getComponent());  
  6.     }  
  7. }  


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对象

[java] view plain copy
  1. private SummaryProvider getSummaryProvider(Tile tile) {  
  2.         if (!mActivity.getPackageName().equals(tile.intent.getComponent().getPackageName())) {  
  3.             // Not within Settings, can't load Summary directly.  
  4.             // TODO: Load summary indirectly.  
  5.             return null;  
  6.         }  
  7.         Bundle metaData = getMetaData(tile);  
  8.         if (metaData == null) {  
  9.             if (DEBUG) Log.d(TAG, "No metadata specified for " + tile.intent.getComponent());  
  10.             return null;  
  11.         }  
  12.         String clsName = metaData.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);  
  13.         if (clsName == null) {  
  14.             if (DEBUG) Log.d(TAG, "No fragment specified for " + tile.intent.getComponent());  
  15.             return null;  
  16.         }  
  17.         try {  
  18.             Class<?> cls = Class.forName(clsName);  
  19.             Field field = cls.getField(SUMMARY_PROVIDER_FACTORY);  
  20.             SummaryProviderFactory factory = (SummaryProviderFactory) field.get(null);  
  21.             return factory.createSummaryProvider(mActivity, this);  
  22.         } catch (ClassNotFoundException e) {  
  23.             if (DEBUG) Log.d(TAG, "Couldn't find " + clsName, e);  
  24.         } catch (NoSuchFieldException e) {  
  25.             if (DEBUG) Log.d(TAG, "Couldn't find " + SUMMARY_PROVIDER_FACTORY, e);  
  26.         } catch (ClassCastException e) {  
  27.             if (DEBUG) Log.d(TAG, "Couldn't cast " + SUMMARY_PROVIDER_FACTORY, e);  
  28.         } catch (IllegalAccessException e) {  
  29.             if (DEBUG) Log.d(TAG, "Couldn't get " + SUMMARY_PROVIDER_FACTORY, e);  
  30.         }  
  31.         return null;  
  32.     }  

以NotificationApps.java为例

[java] view plain copy
  1. public class NotificationApps extends ManageApplications {  
  2.   
  3.     private static class SummaryProvider implements SummaryLoader.SummaryProvider {  
  4.   
  5.         private final Context mContext;  
  6.         private final SummaryLoader mLoader;  
  7.         private final NotificationBackend mNotificationBackend;  
  8.   
  9.         private SummaryProvider(Context context, SummaryLoader loader) {  
  10.             mContext = context;  
  11.             mLoader = loader;  
  12.             mNotificationBackend = new NotificationBackend();  
  13.         }  
  14.   
  15.         @Override  
  16.         public void setListening(boolean listening) {  
  17.             if (listening) {  
  18.                 new AppCounter(mContext) {  
  19.                     @Override  
  20.                     protected void onCountComplete(int num) {  
  21.                         updateSummary(num);  
  22.                     }  
  23.   
  24.                     @Override  
  25.                     protected boolean includeInCount(ApplicationInfo info) {  
  26.                         return mNotificationBackend.getNotificationsBanned(info.packageName,  
  27.                                 info.uid);  
  28.                     }  
  29.                 }.execute();  
  30.             }  
  31.         }  
  32.   
  33.         private void updateSummary(int count) {  
  34.             if (count == 0) {  
  35.                 mLoader.setSummary(this, mContext.getString(R.string.notification_summary_none));  
  36.             } else {  
  37.                 mLoader.setSummary(this, mContext.getResources().getQuantityString(  
  38.                         R.plurals.notification_summary, count, count));  
  39.             }  
  40.         }  
  41.     }  
  42.   
  43.     public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY  
  44.             = new SummaryLoader.SummaryProviderFactory() {  
  45.         @Override  
  46.         public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,  
  47.                                                                    SummaryLoader summaryLoader) {  
  48.             return new SummaryProvider(activity, summaryLoader);  
  49.         }  
  50.     };  
  51. }  



31 setListening

(1) 移除消息Worker.MSG_SET_LISTENING
(2) 发送消息Worker.MSG_SET_LISTENING执行方法32

[java] view plain copy
  1. public void setListening(boolean listening) {  
  2.         if (mListening == listening) return;  
  3.         mListening = listening;  
  4.         // Unregister listeners immediately.  
  5.         for (int i = 0; i < mReceivers.size(); i++) {  
  6.             mActivity.unregisterReceiver(mReceivers.valueAt(i));  
  7.         }  
  8.         mReceivers.clear();  
  9.         mWorker.removeMessages(Worker.MSG_SET_LISTENING);  
  10.         mWorker.obtainMessage(Worker.MSG_SET_LISTENING, listening ? 1 : 00).sendToTarget();  
  11.     }  


32 setListeningW(注意锁)

(1)遍历方法18生成的mSummaryMap键SummaryProvider

(2)将反射fragment类中静态内部类执行setListening方法

[java] view plain copy
  1. private synchronized void setListeningW(boolean listening) {  
  2.     if (mWorkerListening == listening) return;  
  3.     mWorkerListening = listening;  
  4.     if (DEBUG) Log.d(TAG, "Listening " + listening);  
  5.     for (SummaryProvider p : mSummaryMap.keySet()) {  
  6.         try {  
  7.             p.setListening(listening);  
  8.         } catch (Exception e) {  
  9.             Log.d(TAG, "Problem in setListening", e);  
  10.         }  
  11.     }  
  12. }  
到此界面第一次刷新完成。Suggestion这个类我还没研究透彻。所以就没有画到时序图里面。主要类是SuggestionParser.java

再介绍一下Settings.Java的作用


正常我们写很多activity。是不是该这样写:

[java] view plain copy
  1. for(int i = 1 ; i <=需要新建activity的个数;i++) {  
  2.    新建class Activityi extends SettingsActivity  
  3. }  

这样做没有什么不对,也不会对性能有影响。但是SettingsActivity真的是爸爸,儿子们需要做的都被爸爸干完了,所以你会发现许多空白文件。

Google就利用java静态内部类的机制(可以去百度一下)编译就会生成他的宿主类名$静态内部类名 也就是我们manifest看见类似于Settings$WirelessSettings。这样写就避免大量白痴文件。


Google 这次7.0Settings加载逻辑大改其实对于新界面开发更容易了。只要去manifest将你的activity属性配置好。Settings.java声明。目前代码逻辑就能直接把你界面加载完毕。然后你去对应的fragment实现逻辑。这样新界面开发基本不需要改动现有逻辑。真的是厉害了,我的歌!

原创粉丝点击