Launcher3主页面加载显示流程分析

打印 上一主题 下一主题

主题 850|帖子 850|积分 2550

布局布局

抓取布局后,可以看到每个图标是一个DoubleShadowBubbleTextView,父布局是CellLayout、workspace。
   我们可以在CellLayout添加子view打印出调用堆栈信息,可以整体上看页面加载显示流程。

  重要类



  • Launcher.java:主界面,即MainActivity
  • launcher.xml:主界面布局文件
  • LauncherModel.java:管理Launcher状态,包罗加载使命、状态回调等
  • LoaderTask.java:加载使命,是一个Runnable
  • LoaderResults.java:加载结果
  • BgDataModel#Callbacks:数据回调接口,Launcher实现该接口,加载使命通过该接口回调给Launcher
  • LauncherProvider:桌面数据提供者,采用db生存桌面图标数据(包罗排列位置、范例等)
  • LauncherSettings:封装访问LauncherProvider时的uri、column等一些常量,通过ContentResolver来访问LauncherProvider,不直接操作db
  • LoaderCursor:封装cursor操作
流程图


创建Activity

主界面创建的时间,通例的setContentView和findView,创建LauncherModel并开始加载数据
  1. // Launcher.java
  2. protected void onCreate(Bundle savedInstanceState) {
  3.     super.onCreate(savedInstanceState);
  4.     // 1.创建LauncherAppState和LauncherModel
  5.     LauncherAppState app = LauncherAppState.getInstance(this);
  6.     mModel = app.getModel();
  7.    
  8.     // 2.infalte布局文件,找到view
  9.     setupViews();
  10.    
  11.     // 3. 设置loader监听,开始加载数据,加载完成后回调给主界面
  12.     if (!mModel.addCallbacksAndLoad(this)) {
  13.         if (!internalStateHandled) {
  14.             // If we are not binding synchronously, pause drawing until initial bind complete,
  15.             // so that the system could continue to show the device loading prompt
  16.             mOnInitialBindListener = Boolean.FALSE::booleanValue;
  17.         }
  18.     }
  19.    
  20.     // 4. 设置view给Activity
  21.     setContentView(getRootView());
复制代码
加载数据

创建加载使命

mModel.addCallbacksAndLoad将Launcher设置给LauncherModel,然后创建了加载使命
  1. // LauncherModel.java   
  2. /**
  3. * Adds a callbacks to receive model updates
  4. * @return true if workspace load was performed synchronously
  5. */
  6. public boolean addCallbacksAndLoad(@NonNull final Callbacks callbacks) {
  7.     synchronized (mLock) {
  8.         addCallbacks(callbacks);
  9.         return startLoader(new Callbacks[] { callbacks });
  10.     }
  11. }
  12. private boolean startLoader(@NonNull final Callbacks[] newCallbacks) {
  13.     synchronized (mLock) {
  14.         // 1.取消旧的加载任务
  15.         boolean wasRunning = stopLoader(); // 之前没有loader任务,wasRunning为false
  16.         boolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;// 之前没有加载过,bindDirectly为false
  17.         boolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0; // bindAllCallbacks为true, 上一步已经addCallbacks了,所以callbacksList里面包含从Launcher.java传进来的callbacks
  18.         final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;
  19.         if (callbacksList.length > 0) {
  20.             // 2.清空PendingBind
  21.             for (Callbacks cb : callbacksList) {
  22.                 MAIN_EXECUTOR.execute(cb::clearPendingBinds);
  23.             }
  24.             // 3.创建LoaderResults和LoaderTask,开始加载数据
  25.             LoaderResults loaderResults = new LoaderResults(
  26.                     mApp, mBgDataModel, mBgAllAppsList, callbacksList);
  27.             if (bindDirectly) {
  28.                 loaderResults.bindWorkspace(bindAllCallbacks);
  29.                 loaderResults.bindAllApps();
  30.                 loaderResults.bindDeepShortcuts();
  31.                 loaderResults.bindWidgets();
  32.                 return true;
  33.             } else {
  34.                 stopLoader();
  35.                 mLoaderTask = new LoaderTask(
  36.                         mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
  37.                 MODEL_EXECUTOR.post(mLoaderTask);
  38.             }
  39.         }
  40.     }
  41. }
复制代码
整体加载步调

LoaderTask#run中根据范例加载步调分为了5步,图标的加载重要看第一步
  1. // LoaderTask.java
  2. public void run() {
  3.     try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
  4.         // first step
  5.         List<ShortcutInfo> allShortcuts = new ArrayList<>();
  6.         loadWorkspace(allShortcuts, memoryLogger);
  7.         // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
  8.         // sanitizeData should not be invoked if the workspace is loaded from a db different
  9.         // from the main db as defined in the invariant device profile.
  10.         // (e.g. both grid preview and minimal device mode uses a different db)
  11.         if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) {
  12.             sanitizeData();
  13.         }
  14.         mResults.bindWorkspace(true /* incrementBindId */);
  15.         mModelDelegate.workspaceLoadComplete();
  16.         // Notify the installer packages of packages with active installs on the first screen.
  17.         sendFirstScreenActiveInstallsBroadcast();
  18.         // Take a break
  19.         waitForIdle();
  20.         // second step
  21.         List<LauncherActivityInfo> allActivityList;
  22.          allActivityList = loadAllApps();
  23.         mResults.bindAllApps();
  24.         IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
  25.         setIgnorePackages(updateHandler);
  26.         updateHandler.updateIcons(allActivityList,
  27.                 LauncherActivityCachingLogic.newInstance(mApp.getContext()),
  28.                 mApp.getModel()::onPackageIconsUpdated);
  29.         updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
  30.                 mApp.getModel()::onPackageIconsUpdated);
  31.         waitForIdle();
  32.         // third step
  33.         List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
  34.         mResults.bindDeepShortcuts();
  35.         updateHandler.updateIcons(allDeepShortcuts,
  36.                 new ShortcutCachingLogic(), (pkgs, user) -> { });
  37.         waitForIdle();
  38.    
  39.         // fourth step
  40.         List<ComponentWithLabelAndIcon> allWidgetsList =
  41.                 mBgDataModel.widgetsModel.update(mApp, null);
  42.         mResults.bindWidgets();
  43.         updateHandler.updateIcons(allWidgetsList,
  44.                 new ComponentWithIconCachingLogic(mApp.getContext(), true),
  45.                 mApp.getModel()::onWidgetLabelsUpdated);
  46.    
  47.         // fifth step
  48.         loadFolderNames();
  49.         updateHandler.finish();
  50.         mModelDelegate.modelLoadComplete();
  51.         transaction.commit();
  52.     } catch (CancellationException e) {
  53.         ...
  54.     }
  55. }
复制代码
图标解析

loadWorkspace()代码比较多,重要作用是通过ContentResolver查询LauncherProvider中生存的桌面图标信息,然后遍历cursor来解析数据。
   在分析代码的时间,抓到数据流向,可以结合真实的db数据举行分析,其中一些判断容错处置惩罚可以跳过
  重要流程如下:


  • 加载默认的数据(Provider会有判断,只有第一次才会加载)
  • 通过ContentResolver查询全部数据
  • 遍历cursor,根据不同图标范例举行解析,如ITEM_TYPE_APPLICATION代表普通的图标,ITEM_TYPE_FOLDER代表文件夹
  • 对数据举行一些校验,校验通事后将数据添加到mBgDataModel
数据流向:
Db(LauncherProvider) --> Cursor(LoaderCursor) --> List(BgDataModel)
  1. protected void loadWorkspace(
  2.         List<ShortcutInfo> allDeepShortcuts,
  3.         Uri contentUri,
  4.         String selection,
  5.         @Nullable LoaderMemoryLogger logger) {
  6.     final Context context = mApp.getContext();
  7.     final ContentResolver contentResolver = context.getContentResolver();
  8.     // 1. 加载默认的数据(Provider会有判断,只有第一次才会加载)
  9.     LauncherSettings.Settings.call(contentResolver,
  10.             LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
  11.     synchronized (mBgDataModel) {
  12.         mBgDataModel.clear();
  13.         mPendingPackages.clear();
  14.         // 2. 查询所有数据
  15.         final LoaderCursor c = new LoaderCursor(
  16.                 contentResolver.query(contentUri, null, selection, null, null), contentUri,
  17.                 mApp, mUserManagerState);
  18.         final Bundle extras = c.getExtras();
  19.         mDbName = extras == null
  20.                 ? null : extras.getString(LauncherSettings.Settings.EXTRA_DB_NAME);
  21.         try {
  22.             // 2.1 遍历cursor
  23.             final int appWidgetIdIndex = c.getColumnIndexOrThrow(
  24.                     LauncherSettings.Favorites.APPWIDGET_ID);
  25.             final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
  26.                     LauncherSettings.Favorites.APPWIDGET_PROVIDER);
  27.             final int spanXIndex = c.getColumnIndexOrThrow
  28.                     (LauncherSettings.Favorites.SPANX);
  29.             final int spanYIndex = c.getColumnIndexOrThrow(
  30.                     LauncherSettings.Favorites.SPANY);
  31.             final int rankIndex = c.getColumnIndexOrThrow(
  32.                     LauncherSettings.Favorites.RANK);
  33.             final int optionsIndex = c.getColumnIndexOrThrow(
  34.                     LauncherSettings.Favorites.OPTIONS);
  35.             final int sourceContainerIndex = c.getColumnIndexOrThrow(
  36.                     LauncherSettings.Favorites.APPWIDGET_SOURCE);
  37.             WorkspaceItemInfo info;
  38.             LauncherAppWidgetInfo appWidgetInfo;
  39.             LauncherAppWidgetProviderInfo widgetProviderInfo;
  40.             Intent intent;
  41.             String targetPkg;
  42.             List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>();
  43.             while (!mStopped && c.moveToNext()) {
  44.                 try {
  45.                     // 2.2 根据不同图标类型进行解析,ITEM_TYPE_APPLICATION代表普通的图标,ITEM_TYPE_FOLDER代表文件夹
  46.                     switch (c.itemType) {
  47.                     case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
  48.                     case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
  49.                     case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
  50.                         intent = c.parseIntent();
  51.                       // 2.3 解析数据,进行一些校验判断
  52.                         if (info != null) {
  53.                             if (info.itemType
  54.                                     != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
  55.                                 // Skip deep shortcuts; their title and icons have already been
  56.                                 // loaded above.
  57.                                 iconRequestInfos.add(
  58.                                         c.createIconRequestInfo(info, useLowResIcon));
  59.                             }
  60.                             c.applyCommonProperties(info);
  61.                             info.intent = intent;
  62.                             info.rank = c.getInt(rankIndex);
  63.                             info.spanX = 1;
  64.                             info.spanY = 1;
  65.                             info.runtimeStatusFlags |= disabledState;
  66.                             if (isSafeMode && !isSystemApp(context, intent)) {
  67.                                 info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE;
  68.                             }
  69.                                 LauncherActivityInfo activityInfo = c.getLauncherActivityInfo();
  70.                                 if (activityInfo != null) {
  71.                                     info.setProgressLevel(
  72.                                             PackageManagerHelper
  73.                                                 .getLoadingProgress(activityInfo),
  74.                                             PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
  75.                                 }
  76.                             if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
  77.                                 tempPackageKey.update(targetPkg, c.user);
  78.                                 SessionInfo si = installingPkgs.get(tempPackageKey);
  79.                                     if (si == null) {
  80.                                         info.runtimeStatusFlags &=
  81.                                             ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
  82.                                     } else if (activityInfo == null) {
  83.                                         int installProgress = (int) (si.getProgress() * 100);
  84.                                         info.setProgressLevel(
  85.                                                 installProgress,
  86.                                                 PackageInstallInfo.STATUS_INSTALLING);
  87.                                     }
  88.                             }
  89.                             // 3.将数据添加到mBgDataModel
  90.                             c.checkAndAddItem(info, mBgDataModel, logger);
  91.                         } else {
  92.                             throw new RuntimeException("Unexpected null WorkspaceItemInfo");
  93.                         }
  94.                         break;
  95.                     case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
  96.                         FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id);
  97.                         c.applyCommonProperties(folderInfo);
  98.                         // Do not trim the folder label, as is was set by the user.
  99.                         folderInfo.title = c.getString(c.titleIndex);
  100.                         folderInfo.spanX = 1;
  101.                         folderInfo.spanY = 1;
  102.                         folderInfo.options = c.getInt(optionsIndex);
  103.                         // no special handling required for restored folders
  104.                         c.markRestored();
  105.                         c.checkAndAddItem(folderInfo, mBgDataModel, logger);
  106.                         break;
  107.                     }
  108.                 } catch (Exception e) {
  109.                     Log.e(TAG, "Desktop items loading interrupted", e);
  110.                 }
  111.             }
  112.         } finally {
  113.             IOUtils.closeSilently(c);
  114.         }
  115.         // Load delegate items
  116.         mModelDelegate.loadItems(mUserManagerState, shortcutKeyToPinnedShortcuts);
  117.         // Load string cache
  118.         mModelDelegate.loadStringCache(mBgDataModel.stringCache);
  119.         // Remove dead items
  120.         mItemsDeleted = c.commitDeleted();
  121.         // Sort the folder items, update ranks, and make sure all preview items are high res.
  122.         FolderGridOrganizer verifier =
  123.                 new FolderGridOrganizer(mApp.getInvariantDeviceProfile());
  124.         for (FolderInfo folder : mBgDataModel.folders) {
  125.             Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
  126.             verifier.setFolderInfo(folder);
  127.             int size = folder.contents.size();
  128.             // Update ranks here to ensure there are no gaps caused by removed folder items.
  129.             // Ranks are the source of truth for folder items, so cellX and cellY can be ignored
  130.             // for now. Database will be updated once user manually modifies folder.
  131.             for (int rank = 0; rank < size; ++rank) {
  132.                 WorkspaceItemInfo info = folder.contents.get(rank);
  133.                 info.rank = rank;
  134.                 if (info.usingLowResIcon()
  135.                         && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
  136.                         && verifier.isItemInPreview(info.rank)) {
  137.                     mIconCache.getTitleAndIcon(info, false);
  138.                 }
  139.             }
  140.         }
  141.         c.commitRestoredItems();
  142.     }
  143. }
复制代码
可以结合db数据举行分析:


  • container: 表示显示的布局,像普通、文件夹、dock栏等
  • screen: 表示现在第几屏
  • itemType:表示范例,像应用图标、文件夹、快捷方式等

图标显示

到这里,我们已经获取到桌面数据了,下面就是要设置到view显示出来。在上面LoaderTask#run()中,loadWorkspace()获取到了数据,而ui显示的触发在mResults.bindWorkspace(true)
bindWorkspace()先是将数据复制一份,然后遍历callback举行bind。上一步mBgDataModel生存了数据库中数据,mBgDataModel是LauncherModel的一个成员变量,在activity#onCreate时创建的,而activity就是Callbacks,所以到这里都关联了起来。
Launcher (Callbacks)—> LoaderTask —> LoaderResults --> BgDataModel --> Callbacks
  1. // LoaderResults.java
  2. /**
  3. * Binds all loaded data to actual views on the main thread.
  4. */
  5. public void bindWorkspace(boolean incrementBindId) {
  6.     // Save a copy of all the bg-thread collections
  7.     ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
  8.     ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
  9.     final IntArray orderedScreenIds = new IntArray();
  10.     ArrayList<FixedContainerItems> extraItems = new ArrayList<>();
  11.     synchronized (mBgDataModel) {
  12.         workspaceItems.addAll(mBgDataModel.workspaceItems);
  13.         appWidgets.addAll(mBgDataModel.appWidgets);
  14.         orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
  15.         mBgDataModel.extraItems.forEach(extraItems::add);
  16.         if (incrementBindId) {
  17.             mBgDataModel.lastBindId++;
  18.         }
  19.         mMyBindingId = mBgDataModel.lastBindId;
  20.     }
  21.     for (Callbacks cb : mCallbacksList) {
  22.         new WorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
  23.                 workspaceItems, appWidgets, extraItems, orderedScreenIds).bind();
  24.     }
  25. }
复制代码
WorkspaceBinder中bind方法会执行callback回调,回调到activity后,创建BubbleTextView添加到CellLayout。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

科技颠覆者

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表