赞
踩
本文我们详细看看应用列表的加载过程,代码是基于高通7.1。先前的文章我们分析过Launcher3数据的加载,知道应用列表数据加载的起点是在LauncherModel的loadAndBindAllApps()开始的,该方法会调用loadAllApps()。
- packages\apps\Launcher3\src\com\android\launcher3\LauncherModel.java
- private void loadAllApps() {
- ...
- mBgAllAppsList.clear();
- for (UserHandleCompat user : profiles) {
- // Query for the set of apps
- final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
- final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
- if (DEBUG_LOADERS) {
- Log.d(TAG, "getActivityList took "
- + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
- Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
- }
- // Fail if we don't have any apps
- // TODO: Fix this. Only fail for the current user.
- if (apps == null || apps.isEmpty()) {
- return;
- }
- boolean quietMode = mUserManager.isQuietModeEnabled(user);
- // Create the ApplicationInfos
- for (int i = 0; i < apps.size(); i++) {
- LauncherActivityInfoCompat app = apps.get(i);
- // This builds the icon bitmaps.
- mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, quietMode));
- }
- ...
- }
- // Huh? Shouldn't this be inside the Runnable below?
- final ArrayList<AppInfo> added = mBgAllAppsList.added;
- mBgAllAppsList.added = new ArrayList<AppInfo>();
-
- // Post callback on main thread
- mHandler.post(new Runnable() {
- public void run() {
-
- final long bindTime = SystemClock.uptimeMillis();
- final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.bindAllApplications(added);
- if (DEBUG_LOADERS) {
- Log.d(TAG, "bound " + added.size() + " apps in "
- + (SystemClock.uptimeMillis() - bindTime) + "ms");
- }
- } else {
- Log.i(TAG, "not binding apps: no Launcher activity");
- }
- }
- });
- ...
- }
函数中调用LauncherAppsCompat.java的getActivityList方法获取所有安装的应用。LauncherAppsCompat是一个抽象类,不同的android版本有不同的处理方式。
- //Lollipop之前的版本
- packages\apps\Launcher3\src\com\android\launcher3\compat\LauncherAppsCompatV16.java
- public List<LauncherActivityInfoCompat> getActivityList(String packageName,
- UserHandleCompat user) {
- final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- mainIntent.setPackage(packageName);
- List<ResolveInfo> infos = mPm.queryIntentActivities(mainIntent, 0);
- List<LauncherActivityInfoCompat> list =
- new ArrayList<LauncherActivityInfoCompat>(infos.size());
- for (ResolveInfo info : infos) {
- list.add(new LauncherActivityInfoCompatV16(mContext, info));
- }
- return list;
- }
- //以下为Lollipop之后的版本,获取应用的流程
- packages\apps\Launcher3\src\com\android\launcher3\compat\LauncherAppsCompatVL.java
- public List<LauncherActivityInfoCompat> getActivityList(String packageName,
- UserHandleCompat user) {
- List<LauncherActivityInfo> list = mLauncherApps.getActivityList(packageName,
- user.getUser());
- if (list.size() == 0) {
- return Collections.emptyList();
- }
- ArrayList<LauncherActivityInfoCompat> compatList =
- new ArrayList<LauncherActivityInfoCompat>(list.size());
- for (LauncherActivityInfo info : list) {
- compatList.add(new LauncherActivityInfoCompatVL(info));
- }
- return compatList;
- }
- frameworks\base\core\java\android\content\pm\LauncherApps.java
- public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
- logErrorForInvalidProfileAccess(user);
- try {
- return convertToActivityList(mService.getLauncherActivities(mContext.getPackageName(),
- packageName, user), user);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
-
- frameworks\base\services\core\java\com\android\server\pm\LauncherAppsService.java
- @Override
- public ParceledListSlice<ResolveInfo> getLauncherActivities(String callingPackage,
- String packageName, UserHandle user)
- throws RemoteException {
- return queryActivitiesForUser(callingPackage,
- new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_LAUNCHER)
- .setPackage(packageName),
- user);
- }
可以看到最终还是通过PackageManagerService查询出所有安装的应用。之后对mBgAllAppsList填充数据,回调Launcher3的bindAllApplications方法。而该方法只是简单调用了mAppsView.setApps(apps),并没有数据更新时,那个我们常见的Adapter.notifyDataSetChanged()方法调用。别急,我们慢慢来分析,setApps之后做了很多工作,其中比较重要的方法就是
- packages\apps\Launcher3\src\com\android\launcher3\allapps\AlphabeticalAppsList.java
- /**
- * Updates internals when the set of apps are updated.
- */
- private void onAppsUpdated() {
- // Sort the list of apps
- mApps.clear();
- mApps.addAll(mComponentToAppMap.values());
- Collections.sort(mApps, mAppNameComparator.getAppInfoComparator());
- // As a special case for some languages (currently only Simplified Chinese), we may need to
- // coalesce sections
- Locale curLocale = mLauncher.getResources().getConfiguration().locale;
- TreeMap<String, ArrayList<AppInfo>> sectionMap = null;
- boolean localeRequiresSectionSorting = curLocale.equals(Locale.SIMPLIFIED_CHINESE);
- if (localeRequiresSectionSorting) {
- // Compute the section headers. We use a TreeMap with the section name comparator to
- // ensure that the sections are ordered when we iterate over it later
- sectionMap = new TreeMap<>(mAppNameComparator.getSectionNameComparator());
- for (AppInfo info : mApps) {
- // Add the section to the cache
- String sectionName = getAndUpdateCachedSectionName(info.title);
-
- // Add it to the mapping
- ArrayList<AppInfo> sectionApps = sectionMap.get(sectionName);
- if (sectionApps == null) {
- sectionApps = new ArrayList<>();
- sectionMap.put(sectionName, sectionApps);
- }
- sectionApps.add(info);
- }
-
- // Add each of the section apps to the list in order
- List<AppInfo> allApps = new ArrayList<>(mApps.size());
- for (Map.Entry<String, ArrayList<AppInfo>> entry : sectionMap.entrySet()) {
- allApps.addAll(entry.getValue());
- }
-
- mApps.clear();
- mApps.addAll(allApps);
- } else {
- // Just compute the section headers for use below
- for (AppInfo info : mApps) {
- // Add the section to the cache
- getAndUpdateCachedSectionName(info.title);
- }
- }
-
- // Recompose the set of adapter items from the current set of apps
- updateAdapterItems();
- }
一大段的代码都是对mApps进行排序。先按title来排序,如果title一样再对componentName排序,如果是中文则用AlphabeticIndex类来进行处理。具体的就请大家自行看下代码了。我们来看一下最后的updateAdapterItems调用。
- packages\apps\Launcher3\src\com\android\launcher3\allapps\AlphabeticalAppsList.java
- private void updateAdapterItems() {
- SectionInfo lastSectionInfo = null;
- String lastSectionName = null;
- FastScrollSectionInfo lastFastScrollerSectionInfo = null;
- int position = 0;
- int appIndex = 0;
-
- // Prepare to update the list of sections, filtered apps, etc.
- mFilteredApps.clear();
- mFastScrollerSections.clear();
- mAdapterItems.clear();
- mSections.clear();
-
- if (DEBUG_PREDICTIONS) {
- if (mPredictedAppComponents.isEmpty() && !mApps.isEmpty()) {
- mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
- UserHandleCompat.myUserHandle()));
- mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
- UserHandleCompat.myUserHandle()));
- mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
- UserHandleCompat.myUserHandle()));
- mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
- UserHandleCompat.myUserHandle()));
- }
- }
-
- // Add the search divider
- mAdapterItems.add(AdapterItem.asSearchDivder(position++));
-
- // Process the predicted app components
- mPredictedApps.clear();
- if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) {
- for (ComponentKey ck : mPredictedAppComponents) {
- AppInfo info = mComponentToAppMap.get(ck);
- if (info != null) {
- mPredictedApps.add(info);
- } else {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
- Log.e(TAG, "Predicted app not found: " + ck);
- }
- }
- // Stop at the number of predicted apps
- if (mPredictedApps.size() == mNumPredictedAppsPerRow) {
- break;
- }
- }
-
- if (!mPredictedApps.isEmpty()) {
- // Add a section for the predictions
- lastSectionInfo = new SectionInfo();
- lastFastScrollerSectionInfo = new FastScrollSectionInfo("");
- AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo);
- mSections.add(lastSectionInfo);
- mFastScrollerSections.add(lastFastScrollerSectionInfo);
- mAdapterItems.add(sectionItem);
-
- // Add the predicted app items
- for (AppInfo info : mPredictedApps) {
- AdapterItem appItem = AdapterItem.asPredictedApp(position++, lastSectionInfo,
- "", lastSectionInfo.numApps++, info, appIndex++);
- if (lastSectionInfo.firstAppItem == null) {
- lastSectionInfo.firstAppItem = appItem;
- lastFastScrollerSectionInfo.fastScrollToItem = appItem;
- }
- mAdapterItems.add(appItem);
- mFilteredApps.add(info);
- }
-
- mAdapterItems.add(AdapterItem.asPredictionDivider(position++));
- }
- }
-
- // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
- // ordered set of sections
- for (AppInfo info : getFiltersAppInfos()) {
- String sectionName = getAndUpdateCachedSectionName(info.title);
-
- // Create a new section if the section names do not match
- if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) {
- lastSectionName = sectionName;
- lastSectionInfo = new SectionInfo();
- lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName);
- mSections.add(lastSectionInfo);
- mFastScrollerSections.add(lastFastScrollerSectionInfo);
-
- // Create a new section item to break the flow of items in the list
- if (!hasFilter()) {
- AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo);
- mAdapterItems.add(sectionItem);
- }
- }
-
- // Create an app item
- AdapterItem appItem = AdapterItem.asApp(position++, lastSectionInfo, sectionName,
- lastSectionInfo.numApps++, info, appIndex++);
- if (lastSectionInfo.firstAppItem == null) {
- lastSectionInfo.firstAppItem = appItem;
- lastFastScrollerSectionInfo.fastScrollToItem = appItem;
- }
- mAdapterItems.add(appItem);
- mFilteredApps.add(info);
- }
-
- // Append the search market item if we are currently searching
- if (hasFilter()) {
- if (hasNoFilteredResults()) {
- mAdapterItems.add(AdapterItem.asEmptySearch(position++));
- } else {
- mAdapterItems.add(AdapterItem.asMarketDivider(position++));
- }
- mAdapterItems.add(AdapterItem.asMarketSearch(position++));
- }
-
- // Merge multiple sections together as requested by the merge strategy for this device
- mergeSections();
- ...
- // Refresh the recycler view
- if (mAdapter != null) {
- mAdapter.notifyDataSetChanged();
- }
- }
代码很长,耐心多看几遍就可以理解。mPredictedApps是预测apk,就是使用最频繁的几个apk,如果有就先加入到mAdapterItems中;然后就是getFiltersAppInfos(),如果我们正在搜索某个apk,如果关键字匹配,就会返回匹配关键字的所有apk,函数中有一个mSearchResults变量可以用来判断是否正处于搜索状态。如果这个为空则表示不在搜索,此时直接返回排好序的mApps列表。当然函数里面还有一些分割线的相关处理,我们对照下界面布局效果就更好理解。准备好mAdapterItems数据,直接调用我们希望调用的notifyDataSetChanged刷新应用列表。
本文就写到这里了,有不对的地方还请大家评论指出。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。