Launcher2分析-加载Workspace数据和绑定

本文深入探讨了Launcher2的数据加载过程,包括从default_workspace.xml填充数据库、查询favorite表并创建ItemInfo对象。同时,分析了数据绑定阶段,详细阐述了ItemInfo如何根据container属性分类存储,以及ShortcutInfo、FolderInfo和LauncherAppWidgetInfo的构建。此外,还介绍了AppWidgetInfo的构建以及Widget的绑定方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通过调用LoaderTask#startLoader()数据加载和绑定,该方法在Launcher#oncreate()中被调用:

if (sPausedFromUserAction) {
                // If the user leaves launcher, then we should just load items asynchronously when
                // they return.
                mModel.startLoader(true, -1);
            } else {
                // We only load the page synchronously if the user rotates (or triggers a
                // configuration change) while launcher is in the foreground
                mModel.startLoader(true, mWorkspace.getCurrentPage());
            }

其中第二个参数不为-1时,是同步加载,只有桌面正在显示且屏幕发生旋转时才会同步加载。

看startLoader()这个方法源码:

public void startLoader(boolean isLaunching, int synchronousBindPage) {
        synchronized (mLock) {
            if (DEBUG_LOADERS) {
                Log.d(TAG, "startLoader isLaunching=" + isLaunching);
            }

            // Clear any deferred bind-runnables from the synchronized load process
            // We must do this before any loading/binding is scheduled below.
            mDeferredBindRunnables.clear();

            // Don't bother to start the thread if we know it's not going to do anything
            if (mCallbacks != null && mCallbacks.get() != null) {
                // If there is already one running, tell it to stop.
                // also, don't downgrade isLaunching if we're already running
                isLaunching = isLaunching || stopLoaderLocked();
                mLoaderTask = new LoaderTask(mApp, isLaunching);
                if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {
                    mLoaderTask.runBindSynchronousPage(synchronousBindPage);//同步加载时调用runBindSynchronousPage,而不是run()
                } else {
                    sWorkerThread.setPriority(Thread.NORM_PRIORITY);
                    sWorker.post(mLoaderTask);//异步加载,在给线程池运行LoaderTask的run()
                 }
            }
        }
    }


下面来分析异步加载时的数据加载和绑定过程。






数据加载

LauncherModel$LoaderTask#run()

public void run() {
            synchronized (mLock) {
                mIsLoaderTaskRunning = true;
            }

            keep_running: {//一直循环运行
                // Elevate priority when Home launches for the first time to avoid
                // starving at boot time. Staring at a blank home is not cool.
               //... ...

                // First step. Load workspace first, this is necessary since adding of apps from
                // managed profile in all apps is deferred until onResume. See http://b/17336902.
                if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
                loadAndBindWorkspace();//加载桌面数据和绑定

                if (mStopped) {
                    break keep_running;//在onStop调用后跳出循环
                }

                // Whew! Hard work done.  Slow us down, and wait until the UI thread has
                // settled down.
               //... ...
                waitForIdle();

                // Second step. Load all apps.
                if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
                loadAndBindAllApps();加载应用列表数据和绑定

                // Restore the default thread priority after we are done loading items
                //... ...
            }


            // Update the saved icons if necessary
            //... ...

            // Clear out this reference, otherwise we end up holding it until all of the
            // callback runnables are done.
            mContext = null;

            synchronized (mLock) {
                // If we are still the last one to be scheduled, remove ourselves.
                if (mLoaderTask == this) {
                    mLoaderTask = null;
                }
                mIsLoaderTaskRunning = false;
            }
        }
其中loadAndBindWorkspace会调用loadWorkspace()和bindWorkspace(),下面先分析loadWorkspace()。

LauncherModel$LoaderTask#loadWorkspace():

1. mApp.getLauncherProvider().loadDefaultFavoritesIfNecessary(0, false);

从xml文件填充数据库favorite表,如果没有填充过。默认是default_workspace.xml,源码如下:

/**
     * @param workspaceResId that can be 0 to use default or non-zero for specific resource
     */
    synchronized public void loadDefaultFavoritesIfNecessary(int origWorkspaceResId,
            boolean overridePrevious) {
        String spKey = LauncherApplication.getSharedPreferencesKey();
        SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
//如果manifest中没有写上对应的键值对,那么resId返回0
        int restrictionLayoutId = getWorkspaceLayoutIdFromAppRestrictions();
        boolean restrictionLayoutChanged = didRestrictionLayoutChange(sp, restrictionLayoutId);
        overridePrevious |= restrictionLayoutChanged;
        boolean dbCreatedNoWorkspace =
                sp.getBoolean(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED, false);
        if (dbCreatedNoWorkspace || overridePrevious) {//如果需要初始化favorite表,进入
            SharedPreferences.Editor editor = sp.edit();

            // First try layout from app restrictions if it was found
            int workspaceResId = restrictionLayoutId;

            // If the restrictions are not set, use the resource passed to this method
            if (workspaceResId == 0) {//如果restrictionLayoutId为0,就使用调用方法传进来的resId
                workspaceResId = origWorkspaceResId;
            }

            // Use default workspace resource if none provided
            if (workspaceResId == 0) {//如果调用返回传进来的resId为0,去sharedpreference拿,默认是default_workspace
                workspaceResId = sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, R.xml.default_workspace);
            } else {
                editor.putInt(DEFAULT_WORKSPACE_RESOURCE_ID, workspaceResId);
            }

            // Populate favorites table with initial favorites
            editor.remove(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED);
            if (!dbCreatedNoWorkspace && overridePrevious) {
                if (LOGD) Log.d(TAG, "Clearing old launcher database");
                // Workspace has already been loaded, clear the database.
                deleteDatabase();
            }
            mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), workspaceResId);//将xml中的内容写到
### Android 9 Launcher3 工作空间配置与实现细节 #### 加载应用程序图标 `bindAllApplications(final ArrayList<ApplicationInfo> apps)` 方法负责在 All Apps 页面加载所有应用的图标。此方法遍历传入的应用程序列表并为每个应用程序创建视图,最终将这些视图绑定到界面中显示给用户[^3]。 #### 启动Activity的方式 对于启动 Activity 的操作,在 Android 7.0 及之后版本引入了 `ActivityStarter` 类来管理这一过程。通过获取 `ActivityStarter` 对象实例后利用构建者模式设定必要的参数选项,最后调用相应的方法完成 Activity 的启动流程[^2]。 虽然上述描述主要针对较早版本的功能特性,但在 Android 9 中同样适用类似的机制处理工作区内的快捷方式点击事件触发目标组件的行为逻辑。 #### 修改Launcher3的工作空间布局 要自定义或调整 Launcher3 的工作空间配置,通常涉及以下几个方面: - **编辑 XML 布局文件**:位于 `res/layout/` 文件夹下的资源可以被修改以改变视觉外观。 - **重写 Java/Kotlin 源码中的特定行为**:如果希望更改默认功能,则可能需要深入研究源代码并适当覆盖某些函数或属性。 例如,为了动态更新桌面网格大小,可以在对应的类里找到相关变量进行调整,并确保改动不会影响其他部分正常运作。 ```java // 示例:假设有一个名为 Workspace.java 的类用于表示工作区域 public class Workspace extends ViewGroup { private int mNumRows; private int mNumColumns; public void setGridSize(int rows, int columns) { this.mNumRows = rows; this.mNumColumns = columns; requestLayout(); // 请求重新测量绘制 } } ``` #### 自定义项说明 当涉及到更复杂的定制需求时,比如添加新的手势支持或是集成第三方服务接口等功能扩展,就需要更加详细的规划技术方案设计。这往往意味着不仅要熟悉现有的架构体系结构还要掌握最新的 API 更新情况以及最佳实践指导原则。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值