科技颠覆者 发表于 2025-1-6 05:12:01

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

布局布局

抓取布局后,可以看到每个图标是一个DoubleShadowBubbleTextView,父布局是CellLayout、workspace。
   我们可以在CellLayout添加子view打印出调用堆栈信息,可以整体上看页面加载显示流程。
https://i-blog.csdnimg.cn/direct/71429e0b06ee4b8fa3cf47c68fe67f7c.jpeg
重要类



[*]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操作
流程图

https://i-blog.csdnimg.cn/direct/654e4e6952a74b5293b267a97214b024.png
创建Activity

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

创建加载使命

mModel.addCallbacksAndLoad将Launcher设置给LauncherModel,然后创建了加载使命
// LauncherModel.java   
/**
* Adds a callbacks to receive model updates
* @return true if workspace load was performed synchronously
*/
public boolean addCallbacksAndLoad(@NonNull final Callbacks callbacks) {
    synchronized (mLock) {
      addCallbacks(callbacks);
      return startLoader(new Callbacks[] { callbacks });
    }
}

private boolean startLoader(@NonNull final Callbacks[] newCallbacks) {
    synchronized (mLock) {
      // 1.取消旧的加载任务
      boolean wasRunning = stopLoader(); // 之前没有loader任务,wasRunning为false
      boolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;// 之前没有加载过,bindDirectly为false
      boolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0; // bindAllCallbacks为true, 上一步已经addCallbacks了,所以callbacksList里面包含从Launcher.java传进来的callbacks
      final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;
      if (callbacksList.length > 0) {
            // 2.清空PendingBind
            for (Callbacks cb : callbacksList) {
                MAIN_EXECUTOR.execute(cb::clearPendingBinds);
            }
            // 3.创建LoaderResults和LoaderTask,开始加载数据
            LoaderResults loaderResults = new LoaderResults(
                  mApp, mBgDataModel, mBgAllAppsList, callbacksList);
            if (bindDirectly) {
                loaderResults.bindWorkspace(bindAllCallbacks);
                loaderResults.bindAllApps();
                loaderResults.bindDeepShortcuts();
                loaderResults.bindWidgets();
                return true;
            } else {
                stopLoader();
                mLoaderTask = new LoaderTask(
                        mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
                MODEL_EXECUTOR.post(mLoaderTask);
            }
      }
    }
}
整体加载步调

LoaderTask#run中根据范例加载步调分为了5步,图标的加载重要看第一步
// LoaderTask.java
public void run() {
    try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
      // first step
      List<ShortcutInfo> allShortcuts = new ArrayList<>();
      loadWorkspace(allShortcuts, memoryLogger);
      // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
      // sanitizeData should not be invoked if the workspace is loaded from a db different
      // from the main db as defined in the invariant device profile.
      // (e.g. both grid preview and minimal device mode uses a different db)
      if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) {
            sanitizeData();
      }
      mResults.bindWorkspace(true /* incrementBindId */);
      mModelDelegate.workspaceLoadComplete();
      // Notify the installer packages of packages with active installs on the first screen.
      sendFirstScreenActiveInstallsBroadcast();
      // Take a break
      waitForIdle();


      // second step
      List<LauncherActivityInfo> allActivityList;
         allActivityList = loadAllApps();
      mResults.bindAllApps();
      IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
      setIgnorePackages(updateHandler);
      updateHandler.updateIcons(allActivityList,
                LauncherActivityCachingLogic.newInstance(mApp.getContext()),
                mApp.getModel()::onPackageIconsUpdated);
      updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
                mApp.getModel()::onPackageIconsUpdated);
      waitForIdle();


      // third step
      List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
      mResults.bindDeepShortcuts();
      updateHandler.updateIcons(allDeepShortcuts,
                new ShortcutCachingLogic(), (pkgs, user) -> { });
      waitForIdle();
   

      // fourth step
      List<ComponentWithLabelAndIcon> allWidgetsList =
                mBgDataModel.widgetsModel.update(mApp, null);
      mResults.bindWidgets();
      updateHandler.updateIcons(allWidgetsList,
                new ComponentWithIconCachingLogic(mApp.getContext(), true),
                mApp.getModel()::onWidgetLabelsUpdated);
   

      // fifth step
      loadFolderNames();
      updateHandler.finish();
      mModelDelegate.modelLoadComplete();
      transaction.commit();
    } catch (CancellationException e) {
      ...
    }
}
图标解析

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


[*]加载默认的数据(Provider会有判断,只有第一次才会加载)
[*]通过ContentResolver查询全部数据
[*]遍历cursor,根据不同图标范例举行解析,如ITEM_TYPE_APPLICATION代表普通的图标,ITEM_TYPE_FOLDER代表文件夹
[*]对数据举行一些校验,校验通事后将数据添加到mBgDataModel
数据流向:
Db(LauncherProvider) --> Cursor(LoaderCursor) --> List(BgDataModel)
protected void loadWorkspace(
      List<ShortcutInfo> allDeepShortcuts,
      Uri contentUri,
      String selection,
      @Nullable LoaderMemoryLogger logger) {
    final Context context = mApp.getContext();
    final ContentResolver contentResolver = context.getContentResolver();
    // 1. 加载默认的数据(Provider会有判断,只有第一次才会加载)
    LauncherSettings.Settings.call(contentResolver,
            LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
    synchronized (mBgDataModel) {
      mBgDataModel.clear();
      mPendingPackages.clear();
      // 2. 查询所有数据
      final LoaderCursor c = new LoaderCursor(
                contentResolver.query(contentUri, null, selection, null, null), contentUri,
                mApp, mUserManagerState);
      final Bundle extras = c.getExtras();
      mDbName = extras == null
                ? null : extras.getString(LauncherSettings.Settings.EXTRA_DB_NAME);
      try {
            // 2.1 遍历cursor
            final int appWidgetIdIndex = c.getColumnIndexOrThrow(
                  LauncherSettings.Favorites.APPWIDGET_ID);
            final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
                  LauncherSettings.Favorites.APPWIDGET_PROVIDER);
            final int spanXIndex = c.getColumnIndexOrThrow
                  (LauncherSettings.Favorites.SPANX);
            final int spanYIndex = c.getColumnIndexOrThrow(
                  LauncherSettings.Favorites.SPANY);
            final int rankIndex = c.getColumnIndexOrThrow(
                  LauncherSettings.Favorites.RANK);
            final int optionsIndex = c.getColumnIndexOrThrow(
                  LauncherSettings.Favorites.OPTIONS);
            final int sourceContainerIndex = c.getColumnIndexOrThrow(
                  LauncherSettings.Favorites.APPWIDGET_SOURCE);
            WorkspaceItemInfo info;
            LauncherAppWidgetInfo appWidgetInfo;
            LauncherAppWidgetProviderInfo widgetProviderInfo;
            Intent intent;
            String targetPkg;
            List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>();
            while (!mStopped && c.moveToNext()) {
                try {
                  // 2.2 根据不同图标类型进行解析,ITEM_TYPE_APPLICATION代表普通的图标,ITEM_TYPE_FOLDER代表文件夹
                  switch (c.itemType) {
                  case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                  case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                  case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                        intent = c.parseIntent();
                      // 2.3 解析数据,进行一些校验判断
                        if (info != null) {
                            if (info.itemType
                                    != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                              // Skip deep shortcuts; their title and icons have already been
                              // loaded above.
                              iconRequestInfos.add(
                                        c.createIconRequestInfo(info, useLowResIcon));
                            }
                            c.applyCommonProperties(info);
                            info.intent = intent;
                            info.rank = c.getInt(rankIndex);
                            info.spanX = 1;
                            info.spanY = 1;
                            info.runtimeStatusFlags |= disabledState;
                            if (isSafeMode && !isSystemApp(context, intent)) {
                              info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE;
                            }
                              LauncherActivityInfo activityInfo = c.getLauncherActivityInfo();
                              if (activityInfo != null) {
                                    info.setProgressLevel(
                                          PackageManagerHelper
                                                .getLoadingProgress(activityInfo),
                                          PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
                              }
                            if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
                              tempPackageKey.update(targetPkg, c.user);
                              SessionInfo si = installingPkgs.get(tempPackageKey);
                                    if (si == null) {
                                        info.runtimeStatusFlags &=
                                          ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
                                    } else if (activityInfo == null) {
                                        int installProgress = (int) (si.getProgress() * 100);
                                        info.setProgressLevel(
                                                installProgress,
                                                PackageInstallInfo.STATUS_INSTALLING);
                                    }
                            }
                            // 3.将数据添加到mBgDataModel
                            c.checkAndAddItem(info, mBgDataModel, logger);
                        } else {
                            throw new RuntimeException("Unexpected null WorkspaceItemInfo");
                        }
                        break;
                  case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                        FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id);
                        c.applyCommonProperties(folderInfo);
                        // Do not trim the folder label, as is was set by the user.
                        folderInfo.title = c.getString(c.titleIndex);
                        folderInfo.spanX = 1;
                        folderInfo.spanY = 1;
                        folderInfo.options = c.getInt(optionsIndex);
                        // no special handling required for restored folders
                        c.markRestored();
                        c.checkAndAddItem(folderInfo, mBgDataModel, logger);
                        break;
                  }
                } catch (Exception e) {
                  Log.e(TAG, "Desktop items loading interrupted", e);
                }
            }
      } finally {
            IOUtils.closeSilently(c);
      }
      // Load delegate items
      mModelDelegate.loadItems(mUserManagerState, shortcutKeyToPinnedShortcuts);
      // Load string cache
      mModelDelegate.loadStringCache(mBgDataModel.stringCache);
      // Remove dead items
      mItemsDeleted = c.commitDeleted();
      // Sort the folder items, update ranks, and make sure all preview items are high res.
      FolderGridOrganizer verifier =
                new FolderGridOrganizer(mApp.getInvariantDeviceProfile());
      for (FolderInfo folder : mBgDataModel.folders) {
            Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
            verifier.setFolderInfo(folder);
            int size = folder.contents.size();
            // Update ranks here to ensure there are no gaps caused by removed folder items.
            // Ranks are the source of truth for folder items, so cellX and cellY can be ignored
            // for now. Database will be updated once user manually modifies folder.
            for (int rank = 0; rank < size; ++rank) {
                WorkspaceItemInfo info = folder.contents.get(rank);
                info.rank = rank;
                if (info.usingLowResIcon()
                        && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
                        && verifier.isItemInPreview(info.rank)) {
                  mIconCache.getTitleAndIcon(info, false);
                }
            }
      }
      c.commitRestoredItems();
    }
}
可以结合db数据举行分析:


[*]container: 表示显示的布局,像普通、文件夹、dock栏等
[*]screen: 表示现在第几屏
[*]itemType:表示范例,像应用图标、文件夹、快捷方式等
[*]https://i-blog.csdnimg.cn/direct/af326e9ceb0b4c028483cd969c917d52.jpeg
图标显示

到这里,我们已经获取到桌面数据了,下面就是要设置到view显示出来。在上面LoaderTask#run()中,loadWorkspace()获取到了数据,而ui显示的触发在mResults.bindWorkspace(true)
bindWorkspace()先是将数据复制一份,然后遍历callback举行bind。上一步mBgDataModel生存了数据库中数据,mBgDataModel是LauncherModel的一个成员变量,在activity#onCreate时创建的,而activity就是Callbacks,所以到这里都关联了起来。
Launcher (Callbacks)—> LoaderTask —> LoaderResults --> BgDataModel --> Callbacks

// LoaderResults.java
/**
* Binds all loaded data to actual views on the main thread.
*/
public void bindWorkspace(boolean incrementBindId) {
    // Save a copy of all the bg-thread collections
    ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
    ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
    final IntArray orderedScreenIds = new IntArray();
    ArrayList<FixedContainerItems> extraItems = new ArrayList<>();
    synchronized (mBgDataModel) {
      workspaceItems.addAll(mBgDataModel.workspaceItems);
      appWidgets.addAll(mBgDataModel.appWidgets);
      orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
      mBgDataModel.extraItems.forEach(extraItems::add);
      if (incrementBindId) {
            mBgDataModel.lastBindId++;
      }
      mMyBindingId = mBgDataModel.lastBindId;
    }
    for (Callbacks cb : mCallbacksList) {
      new WorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
                workspaceItems, appWidgets, extraItems, orderedScreenIds).bind();
    }
}
WorkspaceBinder中bind方法会执行callback回调,回调到activity后,创建BubbleTextView添加到CellLayout。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Launcher3主页面加载显示流程分析