Android13-Launcer3_数据加载流程

120 阅读9分钟

Launcher,实现了Callbacks接口,提供了绑定应用图标、小部件、所有应用数据等方法

LauncherAppState,存储有InvariantDeviceProfile、LauncherModel、IconCache、Context对象,方便单例调用

InvariantDeviceProfile,桌面图标行数和列数、文件夹图标行数和列数等配置信息

LauncherModel,数据处理的核心类

LoaderTask,负责加载数据和绑定数据

LoaderResults、BaseLoaderResults,回调Launcher相关绑定方法

BgDataModel,存储从数据库中解析处理的数据,ItemInfo集合、LauncherAppWidgetInfo集合、FolderInfo集合

Launcher

    private LauncherModel mModel;
    
    @Override
    @TargetApi(Build.VERSION_CODES.S)
    protected void onCreate(Bundle savedInstanceState) {
		    
       ...
       //从LauncherAppState 获取LauncherModel对象
       LauncherAppState app = LauncherAppState.getInstance(this);
       mModel = app.getModel();
        
        ...
        
        //触发LauncherModel的startLoader()方法调用
        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;
            }
        }
	    
    }

LauncherModel

    private final ArrayList<Callbacks> mCallbacksList = new ArrayList<>(1);
        
    public boolean addCallbacksAndLoad(@NonNull final Callbacks callbacks) {
        synchronized (mLock) {
		 //把参数callbacks添加到mCallbacksList集合中,Launcher实现了callbacks接口
            addCallbacks(callbacks);
            return startLoader(new Callbacks[] { callbacks });

        }
    }
    private boolean startLoader(@NonNull final Callbacks[] newCallbacks) {
        // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
        ItemInstallQueue.INSTANCE.get(mApp.getContext())
                .pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
        synchronized (mLock) {
            // If there is already one running, tell it to stop.
            //判断是否有LoaderTask在运行
            boolean wasRunning = stopLoader();
            boolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;
            //有参数,取newCallbacks
            boolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0;
            final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;

            if (callbacksList.length > 0) {
                // Clear any pending bind-runnables from the synchronized load process.
                for (Callbacks cb : callbacksList) {
                    MAIN_EXECUTOR.execute(cb::clearPendingBinds);
                }

                LoaderResults loaderResults = new LoaderResults(
                        mApp, mBgDataModel, mBgAllAppsList, callbacksList);
                //已经加载过数据,直接进行绑定数据
                if (bindDirectly) {
                    // Divide the set of loaded items into those that we are binding synchronously,
                    // and everything else that is to be bound normally (asynchronously).
                    loaderResults.bindWorkspace(bindAllCallbacks);
                    // For now, continue posting the binding of AllApps as there are other
                    // issues that arise from that.
                    loaderResults.bindAllApps();
                    loaderResults.bindDeepShortcuts();
                    loaderResults.bindWidgets();
                    return true;
                } else {
                    stopLoader();
                    //LoaderTask实现了Runnable接口
                    mLoaderTask = new LoaderTask(
                            mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);

                    // Always post the loader task, instead of running directly
                    // (even on same thread) so that we exit any nested synchronized blocks
                    //MODEL_EXECUTOR即LooperExecutor,内部有Handler,Handler接收的是HandlerThread的Looper对象
                    //在execute()通过Handler让LoaderTask在子线程运行
                    MODEL_EXECUTOR.post(mLoaderTask);
                }
            }
        }
        return false;
    }

LoaderTask

    public void run() {
        synchronized (this) {
            // Skip fast if we are already stopped.
            //stopLocked()方法会把mStopped置为false
            if (mStopped) {
                return;
            }
        }

        Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
        TimingLogger logger = new TimingLogger(TAG, "run");
        LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
        try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
            List<ShortcutInfo> allShortcuts = new ArrayList<>();
            Trace.beginSection("LoadWorkspace");
            try {
                //加载数据
                loadWorkspace(allShortcuts, memoryLogger);
            } finally {
                Trace.endSection();
            }
            logASplit(logger, "loadWorkspace");

            // 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)) {
                verifyNotStopped();
                sanitizeData();
                logASplit(logger, "sanitizeData");
            }
            //mStopped为true,则抛出一个取消异常,中断往下执行
            verifyNotStopped();
            //应用图标数据绑定到控件上
            mResults.bindWorkspace(true /* incrementBindId */);
            logASplit(logger, "bindWorkspace");

            mModelDelegate.workspaceLoadComplete();
            // Notify the installer packages of packages with active installs on the first screen.
            sendFirstScreenActiveInstallsBroadcast();
            logASplit(logger, "sendFirstScreenActiveInstallsBroadcast");

            // Take a break
            waitForIdle();
            logASplit(logger, "step 1 complete");
            verifyNotStopped();

            // second step
            Trace.beginSection("LoadAllApps");
            List<LauncherActivityInfo> allActivityList;
            try {
               //集中应用列表的数据
               allActivityList = loadAllApps();
            } finally {
                Trace.endSection();
            }
            logASplit(logger, "loadAllApps");
            verifyNotStopped();
            //绑定应用列表的数据
            mResults.bindAllApps();
            logASplit(logger, "bindAllApps");

            verifyNotStopped();
            IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
            setIgnorePackages(updateHandler);
            updateHandler.updateIcons(allActivityList,
                    LauncherActivityCachingLogic.newInstance(mApp.getContext()),
                    mApp.getModel()::onPackageIconsUpdated);
            logASplit(logger, "update icon cache");

            verifyNotStopped();
            logASplit(logger, "save shortcuts in icon cache");
            updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
                    mApp.getModel()::onPackageIconsUpdated);

            // Take a break
            waitForIdle();
            logASplit(logger, "step 2 complete");
            verifyNotStopped();

            // third step
            List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
            logASplit(logger, "loadDeepShortcuts");

            verifyNotStopped();
            mResults.bindDeepShortcuts();
            logASplit(logger, "bindDeepShortcuts");

            verifyNotStopped();
            logASplit(logger, "save deep shortcuts in icon cache");
            updateHandler.updateIcons(allDeepShortcuts,
                    new ShortcutCachingLogic(), (pkgs, user) -> { });

            // Take a break
            waitForIdle();
            logASplit(logger, "step 3 complete");
            verifyNotStopped();

            // fourth step
            List<ComponentWithLabelAndIcon> allWidgetsList =
                    mBgDataModel.widgetsModel.update(mApp, null);
            logASplit(logger, "load widgets");

            verifyNotStopped();
            mResults.bindWidgets();
            logASplit(logger, "bindWidgets");
            verifyNotStopped();

            updateHandler.updateIcons(allWidgetsList,
                    new ComponentWithIconCachingLogic(mApp.getContext(), true),
                    mApp.getModel()::onWidgetLabelsUpdated);
            logASplit(logger, "save widgets in icon cache");

            // fifth step
            loadFolderNames();

            verifyNotStopped();
            updateHandler.finish();
            logASplit(logger, "finish icon update");

            mModelDelegate.modelLoadComplete();
            transaction.commit();
            memoryLogger.clearLogs();
        } catch (CancellationException e) {
            // Loader stopped, ignore
            logASplit(logger, "Cancelled");
        } catch (Exception e) {
            memoryLogger.printLogs();
            throw e;
        } finally {
            logger.dumpToLog();
        }
        TraceHelper.INSTANCE.endSection(traceToken);
    }
    private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts, LoaderMemoryLogger logger) {
        //CONTENT_URI为content://com.android.launcher3.settings/favorites
        loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI,
                null /* selection */, logger);
    }
    protected void loadWorkspace(
            List<ShortcutInfo> allDeepShortcuts,
            Uri contentUri,
            String selection,
            @Nullable LoaderMemoryLogger logger) {
            
        ...
            
        if (clearDb) {
            Log.d(TAG, "loadWorkspace: resetting launcher database");
            //调到LauncherProvider的call方法中的METHOD_CREATE_EMPTY_DB逻辑
            LauncherSettings.Settings.call(contentResolver,
                    LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
        }

        Log.d(TAG, "loadWorkspace: loading default favorites");
        //调到LauncherProvider的call方法中的METHOD_LOAD_DEFAULT_FAVORITES逻辑
        LauncherSettings.Settings.call(contentResolver,
                LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
        
        synchronized (mBgDataModel) {
            mBgDataModel.clear();
            mPendingPackages.clear();
            
        ...
            
        //contentResolver.query(contentUri 查询的是favorites表的数据
        //在构造方法通过游标获取一些列的索引
        final LoaderCursor c = new LoaderCursor(
                    contentResolver.query(contentUri, null, selection, null, null), contentUri,
                    mApp, mUserManagerState);
        
        //保存图标、图标快捷方式的数据类
        WorkspaceItemInfo info;
        //小部件相关的数据类
        LauncherAppWidgetInfo appWidgetInfo;
        
        //moveToNext会从表中获取一些数据
        while (!mStopped && c.moveToNext()) {
		...
		switch (c.itemType) {
                //应用图标的快捷方式
                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                //应用图标
                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                //长按图标,从应用信息拖出的快捷方式
                case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                
                    ...
		                
                    if (c.restoreFlag != 0) {
                        // Already verified above that user is same as default user
                        info = c.getRestoredItemInfo(intent);
                    } else if (c.itemType ==
                    LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
                        //构建应用图标的WorkspaceItemInfo
                        info = c.getAppShortcutInfo(
                                intent,
                                allowMissingTarget,
                                useLowResIcon,
                                !FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get());
                    } else if (c.itemType ==
                    LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {

                        ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
                        if (unlockedUsers.get(c.serialNumber)) {
                            ShortcutInfo pinnedShortcut =
                                    shortcutKeyToPinnedShortcuts.get(key);
                            if (pinnedShortcut == null) {
                                // The shortcut is no longer valid.
                                c.markDeleted("Pinned shortcut not found");
                                continue;
                            }
                            info = new WorkspaceItemInfo(pinnedShortcut, context);
                            // If the pinned deep shortcut is no longer published,
                            // use the last saved icon instead of the default.
                            mIconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon);

                            if (pmHelper.isAppSuspended(
                                    pinnedShortcut.getPackage(), info.user)) {
                                info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
                            }
                            intent = info.getIntent();
                            allDeepShortcuts.add(pinnedShortcut);
                        } else {
                            // Create a shortcut info in disabled mode for now.
                            info = c.loadSimpleWorkspaceItem();
                            info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
                        }
                    } else { // item type == ITEM_TYPE_SHORTCUT
                        //应用图标快捷方式的WorkspaceItemInfo 
                        info = c.loadSimpleWorkspaceItem();

                        // Shortcuts are only available on the primary profile
                        if (!TextUtils.isEmpty(targetPkg)
                                && pmHelper.isAppSuspended(targetPkg, c.user)) {
                            disabledState |= FLAG_DISABLED_SUSPENDED;
                        }
                        info.options = c.getInt(optionsIndex);

                        // App shortcuts that used to be automatically added to Launcher
                        // didn't always have the correct intent flags set, so do that
                        // here
                        if (intent.getAction() != null &&
                                intent.getCategories() != null &&
                                intent.getAction().equals(Intent.ACTION_MAIN) &&
                                intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
                            intent.addFlags(
                                    Intent.FLAG_ACTIVITY_NEW_TASK |
                                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
                        }

                    }

                    ...
                    if (info != null) {

                        //WorkspaceItemInfo添加到mBgDataModel
                        c.checkAndAddItem(info, mBgDataModel, logger);
                    }

            //文件夹
            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();

                //也是添加到mBgDataModel中
                c.checkAndAddItem(folderInfo, mBgDataModel, logger);
            break;


            case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
                ...
                c.checkAndAddItem(appWidgetInfo, mBgDataModel);

从数据库表中获取数据封装到对应的WorkspaceItemInfo、LauncherAppWidgetInfo、FolderInfo 数据类,然后添加到BgDataModel对象中

LoaderTask的run()方法中 mResults.bindWorkspace(true /* incrementBindId */)的逻辑

LoaderResults,继承BaseLoaderResults

    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<>();

        //把BgDataModel的数据复制到新的集合中
        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;
        }

        //从Launcher的onCreate()方法入口,Launcher是实现了Callbacks 接口
        for (Callbacks cb : mCallbacksList) {
            new WorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
                    workspaceItems, appWidgets, extraItems, orderedScreenIds).bind();
        }
    }

WorkspaceBinder,是BaseLoaderResults的内部类

        private void bind() {
            final IntSet currentScreenIds =
                    mCallbacks.getPagesToBindSynchronously(mOrderedScreenIds);
            Objects.requireNonNull(currentScreenIds, "Null screen ids provided by " + mCallbacks);

            // Separate the items that are on the current screen, and all the other remaining items
            //要显示页的WorkspaceItems集合
            ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
            //其他页的WorkspaceItems集合
            ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
            ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
            ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();

            if (TestProtocol.sDebugTracing) {
                Log.d(TestProtocol.NULL_INT_SET, "bind (1) currentScreenIds: "
                        + currentScreenIds
                        + ", pointer: "
                        + mCallbacks
                        + ", name: "
                        + mCallbacks.getClass().getName());
            }
            //过滤数据
            filterCurrentWorkspaceItems(currentScreenIds, mWorkspaceItems, currentWorkspaceItems,
                    otherWorkspaceItems);
            if (TestProtocol.sDebugTracing) {
                Log.d(TestProtocol.NULL_INT_SET, "bind (2) currentScreenIds: "
                        + currentScreenIds);
            }
            filterCurrentWorkspaceItems(currentScreenIds, mAppWidgets, currentAppWidgets,
                    otherAppWidgets);
            final InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
            sortWorkspaceItemsSpatially(idp, currentWorkspaceItems);
            sortWorkspaceItemsSpatially(idp, otherWorkspaceItems);

            // Tell the workspace that we're about to start binding items
            //切换到主线程执行Callbacks接口(Launcher实现了)的clearPendingBinds()和startBinding()方法
            executeCallbacksTask(c -> {
                c.clearPendingBinds();
                //workspace会移除所有的页面,然后添加一个页面
                c.startBinding();
            }, mUiExecutor);

            // Bind workspace screens
            //workspace添加所有的页面
            executeCallbacksTask(c -> c.bindScreens(mOrderedScreenIds), mUiExecutor);

            // Load items on the current page.
            //绑定要显示页的应用图标数据
            bindWorkspaceItems(currentWorkspaceItems, mUiExecutor);
            ////绑定要显示页的小部件数据
            bindAppWidgets(currentAppWidgets, mUiExecutor);
            mExtraItems.forEach(item ->
                    executeCallbacksTask(c -> c.bindExtraContainerItems(item), mUiExecutor));

            RunnableList pendingTasks = new RunnableList();
            Executor pendingExecutor = pendingTasks::add;
            //绑定其他页数据
            bindWorkspaceItems(otherWorkspaceItems, pendingExecutor);
            bindAppWidgets(otherAppWidgets, pendingExecutor);
            executeCallbacksTask(c -> c.finishBindingItems(currentScreenIds), pendingExecutor);
            pendingExecutor.execute(
                    () -> {
                        MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
                        ItemInstallQueue.INSTANCE.get(mApp.getContext())
                                .resumeModelPush(FLAG_LOADER_RUNNING);
                    });

            executeCallbacksTask(
                    c -> {
                        MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                        c.onInitialBindComplete(currentScreenIds, pendingTasks);
                    }, mUiExecutor);

            mCallbacks.bindStringCache(mBgDataModel.stringCache.clone());
        }
        private void bindWorkspaceItems(
                final ArrayList<ItemInfo> workspaceItems, final Executor executor) {
            // Bind the workspace items
            int count = workspaceItems.size();
            for (int i = 0; i < count; i += ITEMS_CHUNK) {
                final int start = i;
                final int chunkSize = (i + ITEMS_CHUNK <= count) ? ITEMS_CHUNK : (count - i);
                executeCallbacksTask(
                        //调用到Launcher的bindItems
                        c -> c.bindItems(workspaceItems.subList(start, start + chunkSize), false),
                        executor);
            }
        }

Launcher

    public void bindItems(
            final List<ItemInfo> items,
            final boolean forceAnimateIcons,
            final boolean focusFirstItemForAccessibility) {
        // Get the list of added items and intersect them with the set of items here
        final Collection<Animator> bounceAnims = new ArrayList<>();
        boolean canAnimatePageChange = canAnimatePageChange();
        Workspace<?> workspace = mWorkspace;
        int newItemsScreenId = -1;
        int end = items.size();
        View newView = null;
        //遍历ItemInfo集合,WorkspaceItemInfo 是ItemInfo的子类
        for (int i = 0; i < end; i++) {
            final ItemInfo item = items.get(i);

            // Short circuit if we are loading dock items for a configuration which has no dock
            if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
                    mHotseat == null) {
                continue;
            }

            final View view;
            //不同Item的类型处理
            switch (item.itemType) {
                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
                    WorkspaceItemInfo info = (WorkspaceItemInfo) item;
                    //构建控件,这里是BubbleTextView
                    view = createShortcut(info);
                    break;
                }
                case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
                    view = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, this,
                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
                            (FolderInfo) item);
                    break;
                }
                case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: {
                    view = inflateAppWidget((LauncherAppWidgetInfo) item);
                    if (view == null) {
                        continue;
                    }
                    break;
                }
                default:
                    throw new RuntimeException("Invalid Item Type");
            }

            /*
             * Remove colliding items.
             */
            if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
                CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
                if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
                    View v = cl.getChildAt(item.cellX, item.cellY);
                    if (v == null) {
                        Log.e(TAG, "bindItems failed when removing colliding item=" + item);
                    }
                    Object tag = v.getTag();
                    String desc = "Collision while binding workspace item: " + item
                            + ". Collides with " + tag;
                    if (FeatureFlags.IS_STUDIO_BUILD) {
                        throw (new RuntimeException(desc));
                    } else {
                        getModelWriter().deleteItemFromDatabase(item, desc);
                        if (TestProtocol.sDebugTracing) {
                            Log.d(TestProtocol.MISSING_PROMISE_ICON,
                                    TAG + "bindItems failed for item=" + item);
                        }
                        continue;
                    }
                }
            }
            //添加到页面,也就是cellLayout中
            workspace.addInScreenFromBind(view, item);
            if (forceAnimateIcons) {
                // Animate all the applications up now
                view.setAlpha(0f);
                view.setScaleX(0f);
                view.setScaleY(0f);
                bounceAnims.add(createNewAppBounceAnimation(view, i));
                newItemsScreenId = item.screenId;
            }

            if (newView == null) {
                newView = view;
            }
        }

        View viewToFocus = newView;
        // Animate to the correct pager
        if (forceAnimateIcons && newItemsScreenId > -1) {
            AnimatorSet anim = new AnimatorSet();
            anim.playTogether(bounceAnims);
            if (focusFirstItemForAccessibility && viewToFocus != null) {
                anim.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        viewToFocus.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
                    }
                });
            }

            int currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage());
            final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newItemsScreenId);
            final Runnable startBounceAnimRunnable = anim::start;

            if (canAnimatePageChange && newItemsScreenId != currentScreenId) {
                // We post the animation slightly delayed to prevent slowdowns
                // when we are loading right after we return to launcher.
                mWorkspace.postDelayed(new Runnable() {
                    public void run() {
                        if (mWorkspace != null) {
                            closeOpenViews(false);

                            mWorkspace.snapToPage(newScreenIndex);
                            mWorkspace.postDelayed(startBounceAnimRunnable,
                                    NEW_APPS_ANIMATION_DELAY);
                        }
                    }
                }, NEW_APPS_PAGE_MOVE_DELAY);
            } else {
                mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY);
            }
        } else if (focusFirstItemForAccessibility && viewToFocus != null) {
            viewToFocus.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
        }
        workspace.requestLayout();
    }
    View createShortcut(WorkspaceItemInfo info) {
        // This can be called before PagedView#pageScrollsInitialized returns true, so use the
        // first page, which we always assume to be present.
        return createShortcut((ViewGroup) mWorkspace.getChildAt(0), info);
    }

    /**
     * Creates a view representing a shortcut inflated from the specified resource.
     *
     * @param parent The group the shortcut belongs to. This is not necessarily the group where
     *               the shortcut should be added.
     * @param info   The data structure describing the shortcut.
     * @return A View inflated from layoutResId.
     */
    public View createShortcut(ViewGroup parent, WorkspaceItemInfo info) {
        //app_icon布局文件是DoubleShadowBubbleTextView
        BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
                .inflate(R.layout.app_icon, parent, false);
        //设置图标、名字
        favorite.applyFromWorkspaceItem(info);
        //设置点击事件
        favorite.setOnClickListener(getItemOnClickListener());
        favorite.setOnFocusChangeListener(mFocusHandler);
        return favorite;
    }

BubbleTextView

    @UiThread
    public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
        applyIconAndLabel(info);
        setItemInfo(info);
        applyLoadingState(promiseStateChanged);
        applyDotState(info, false /* animate */);
        setDownloadStateContentDescription(info, info.getProgressLevel());
    }
	  //图标
	  @UiThread
    protected void applyIconAndLabel(ItemInfoWithIcon info) {
        boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER
                || mDisplay == DISPLAY_TASKBAR;
        int flags = useTheme ? FLAG_THEMED : 0;
        if (mHideBadge) {
            flags |= FLAG_NO_BADGE;
        }
        FastBitmapDrawable iconDrawable = info.newIcon(getContext(), flags);
        mDotParams.appColor = iconDrawable.getIconColor();
        mDotParams.dotColor = getContext().getResources()
                .getColor(android.R.color.system_accent3_200, getContext().getTheme());
        setIcon(iconDrawable);
        applyLabel(info);
    }

    //名字
    @UiThread
    private void applyLabel(ItemInfoWithIcon info) {
        setText(info.title);
        if (info.contentDescription != null) {
            setContentDescription(info.isDisabled()
                    ? getContext().getString(R.string.disabled_app_label, info.contentDescription)
                    : info.contentDescription);
        }
    }

Workspace,实现了WorkspaceLayoutManager

WorkspaceLayoutManager

    default void addInScreenFromBind(View child, ItemInfo info) {
        int x = info.cellX;
        int y = info.cellY;
        if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
                || info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
            int screenId = info.screenId;
            x = getHotseat().getCellXFromOrder(screenId);
            y = getHotseat().getCellYFromOrder(screenId);
        }
        addInScreen(child, info.container, info.screenId, x, y, info.spanX, info.spanY);
    }
        /**
     * Adds the specified child in the specified screen. The position and dimension of
     * the child are defined by x, y, spanX and spanY.
     *
     * @param child The child to add in one of the workspace's screens.
     * @param screenId The screen in which to add the child.
     * @param x The X position of the child in the screen's grid.
     * @param y The Y position of the child in the screen's grid.
     * @param spanX The number of cells spanned horizontally by the child.
     * @param spanY The number of cells spanned vertically by the child.
     */
    default void addInScreen(View child, int container, int screenId, int x, int y,
            int spanX, int spanY) {
        if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
            if (getScreenWithId(screenId) == null) {
                Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
                // DEBUGGING - Print out the stack trace to see where we are adding from
                new Throwable().printStackTrace();
                return;
            }
        }
        if (EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) {
            // This should never happen
            throw new RuntimeException("Screen id should not be extra empty screen: " + screenId);
        }

        final CellLayout layout;
        if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
                || container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
            layout = getHotseat();

            // Hide folder title in the hotseat
            if (child instanceof FolderIcon) {
                ((FolderIcon) child).setTextVisible(false);
            }
        } else {
            // Show folder title if not in the hotseat
            if (child instanceof FolderIcon) {
                ((FolderIcon) child).setTextVisible(true);
            }
            //获取对应的页面,也就是CellLayout
            layout = getScreenWithId(screenId);
        }

        ViewGroup.LayoutParams genericLp = child.getLayoutParams();
        CellLayoutLayoutParams lp;
        if (genericLp == null || !(genericLp instanceof CellLayoutLayoutParams)) {
            lp = new CellLayoutLayoutParams(x, y, spanX, spanY, screenId);
        } else {
            lp = (CellLayoutLayoutParams) genericLp;
            lp.cellX = x;
            lp.cellY = y;
            lp.cellHSpan = spanX;
            lp.cellVSpan = spanY;
        }

        if (spanX < 0 && spanY < 0) {
            lp.isLockedToGrid = false;
        }

        // Get the canonical child id to uniquely represent this view in this screen
        ItemInfo info = (ItemInfo) child.getTag();
        int childId = info.getViewId();

        boolean markCellsAsOccupied = !(child instanceof Folder);
        //添加到CellLayout
        if (!layout.addViewToCellLayout(child, -1, childId, lp, markCellsAsOccupied)) {
            // TODO: This branch occurs when the workspace is adding views
            // outside of the defined grid
            // maybe we should be deleting these items from the LauncherModel?
            Log.e(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
        }

        child.setHapticFeedbackEnabled(false);
        child.setOnLongClickListener(getWorkspaceChildOnLongClickListener());
        if (child instanceof DropTarget) {
            onAddDropTarget((DropTarget) child);
        }
    }

Launcher通过LauncherModel去加载数据,LauncherModel判断是否已经加载过数据,如果没有,会创建一个LoaderTask对象,它实现了Runnable接口,然后交给线程池,在子线程中去加载数据

LoaderTask的run()方法调用loadWorkspace()方法从数据库中表favorites获取数据,根据不同的类型封装到不同的数据类中,比如应用图标是WorkspaceItemInfo,然后存储到BgDataModel的集合中

run()方法接着调用bindWorkspace()方法进行绑定, 会先进行数据过滤,绑定第一页的数据,再绑定其他页的数据,切换到主线程,调用Launcher的bindItems()方法进行绑定

bindItems()方法根据不同的Item类型创建不同的View,比如应用图标是DoubleShadowBubbleTextView控件,然后设置数据、点击事件,接着通过Workspace添加到页面中