Launcher3源码分析(LauncherModel加载数据)

LauncherModel继承BroadcastReceiver,显然是一个广播接收者。在上一篇Launcher的启动中讲到桌面数据的加载工作是在LauncherModel中执行的,那么它是如何加载数据的呢?下面将分析Launcher和LauncherModel的通讯方式以及LauncherModel加载桌面数据的过程。

首先分析的是Launcher和LauncherModel的通讯方式:

(1)LauncherModel.Callback回调接口:LauncherModel通过接口输送已经加载完成的数据给Launcher。

//在Launcher.java中持有LauncherModel对象
mModel = app.setLauncher(this);

继续分析setLauncher(Launcher launcher)方法

//返回LauncherModel
LauncherModel setLauncher(Launcher launcher) {

        getLauncherProvider().setLauncherProviderChangeListener(launcher);
        mModel.initialize(launcher);
        mAccessibilityDelegate = ((launcher != null) && Utilities.isLmpOrAbove()) ?
            new LauncherAccessibilityDelegate(launcher) : null;
        return mModel;
    }

以上方法引用了此LauncherModel中的initialize(Callbacks callbacks);方法。且到LauncherModel中分析

public void initialize(Callbacks callbacks) {

        synchronized (mLock) {
            // Disconnect any of the callbacks and drawables associated with ItemInfos on the
            // workspace to prevent leaking Launcher activities on orientation change.
            unbindItemInfosAndClearQueuedBindRunnables();
            mCallbacks = new WeakReference<Callbacks>(callbacks);
        }
    }

发现传入的参数是Callbacks对象,而Launcher实现了LauncherModel.Callbacks接口,显然LauncherModel和Launcher就建立了联系。

从而完成了Launcher在LauncherModel中注册接口,将接口实现的引用注册到LauncherModel中,并且返回已经完成初始化的LauncherModel的引用。LauncherModel的所有操作结果都会通过callbacks中定义的各个回调接口中的方法通知给Launcher,并由Launcher分发给不同的桌面组件或者Launcher自身。

LauncherModel提供的Callbacks接口:

public interface Callbacks {
    //如果Launcher在加载完成之前被强制暂停,那么需要通过这个回调方法通知Launcher
    //在它再次显示的时候重新执行加载过程
    public boolean setLoadOnResume();
    //获取当前屏幕序号
    public int getCurrentWorkspaceScreen();
    //启动桌面数据绑定
    public void startBinding();
    //批量绑定桌面组件:快捷方式列表,列表的开始位置,列表结束的位置,是否使用动画
    public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
                          boolean forceAnimateIcons);
    //批量绑定桌面页,orderedScreenIds 序列化后的桌面页列表
    public void bindScreens(ArrayList<Long> orderedScreenIds);
    public void bindAddScreens(ArrayList<Long> orderedScreenIds);
    //批量绑定文件夹,folders 文件夹映射列表
    public void bindFolders(LongArrayMap<FolderInfo> folders);
    //绑定任务完成
    public void finishBindingItems();
    //批量绑定小部件,info 需要绑定到桌面上的小部件信息
    public void bindAppWidget(LauncherAppWidgetInfo info);
    //绑定应用程序列表界面的应用程序信息,apps 需要绑定到应用程序列表中的应用程序列表
    public void bindAllApplications(ArrayList<AppInfo> apps);
    // Add folders in all app list.
    public void bindAllApplications2Folder(ArrayList<AppInfo> apps, ArrayList<ItemInfo> items);
    //批量添加组件
    public void bindAppsAdded(ArrayList<Long> newScreens,
                              ArrayList<ItemInfo> addNotAnimated,
                              ArrayList<ItemInfo> addAnimated,
                              ArrayList<AppInfo> addedApps);
    //批量更新应用程序相关的快捷方式或者入口
    public void bindAppsUpdated(ArrayList<AppInfo> apps);
    public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated,
            ArrayList<ShortcutInfo> removed, UserHandleCompat user);
    public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
    public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
    // 从桌面移除一些组件,当应用程序被移除或者禁用的时候调用
    public void bindComponentsRemoved(ArrayList<String> packageNames,
                    ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
    public void bindAllPackages(WidgetsModel model);
    //全局搜索或者搜索属性更新
    public void bindSearchablesChanged();
    public boolean isAllAppsButtonRank(int rank);
    /** * 指示正在绑定的页面 * @param page 桌面页序号 */
    public void onPageBoundSynchronously(int page);
    //输出当前Launcher信息到本地文件中
    public void dumpLogsToLocalData();
}

(2)线程处理

@Thunk DeferredHandler mHandler = new DeferredHandler();
@Thunk LoaderTask mLoaderTask;
@Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
@Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());

Launcher和LauncherModel运行在Launcher这个应用程序的主线程中, sWorkerThread只是Launcher应用程序主线程下的一个子线程,对于线程和线程之间的消息交互,一个比较好的方案是将任务抛到目标线程的处理器中,为此,LauncherModel为sWorkerThread在主线程中创建了一个处理器,以实现sWorkerThread和Launcher所在进程之间的信息交互。

(3)广播接口:

LauncherModel继承了BroadcastReceiver,因此可以接收来自Launcher或其他地方的广播,在onReceive(Context context, Intent intent)方法中做出相应的处理。

来个分割线,分析上面加载绑定数据过程的几个方法:

LauncherModel负责桌面数据的加载,即调用startLoader方法启动线程,加载数据。

public void startLoader(int synchronousBindPage) {
    startLoader(synchronousBindPage, LOADER_FLAG_NONE);
}
//启动LoaderTask线程里面的run方法,sWorker是一个Handler对象,用于启动线程的run方法
public void startLoader(int synchronousBindPage, int loadFlags) {
    // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
    InstallShortcutReceiver.enableInstallQueue();
    synchronized (mLock) {
        // Clear any deferred bind-runnables from the synchronized load process
        // We must do this before any loading/binding is scheduled below.
        //
        //如果当前只是对桌面的第一个页面进行数据刷新,那么这个接口的调用会被封装成为一个任务加载到一个消息队列中
        //等待后续所有任务完成后才统一执行,如果这个任务还没有得到执行,而新的刷新页面的请求已经来到,那么LauncherModel
        //在启动加载之前会将消息队列清空,以确保所在任务都执行完成后,Launcher才会得到通知。
        synchronized (mDeferredBindRunnables) {
            //mDeferredBindRunnables是一个Runnable的列表(ArrayList),当LauncherModel的加载任务完成后,这里将会保存发往Launcher的通知,封装在一个Runnable中,加入该表
            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.
            stopLoaderLocked();
            mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags);
            if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
                    && mAllAppsLoaded && mWorkspaceLoaded && !mIsLoaderTaskRunning) {
                //同步加载
                mLoaderTask.runBindSynchronousPage(synchronousBindPage);
            } else {
                sWorkerThread.setPriority(Thread.NORM_PRIORITY);
                //异步加载
                sWorker.post(mLoaderTask);
            }
        }
    }
}

接下来看LoaderTask的run方法。

public void run() {
    synchronized (mLock) {
        if (mStopped) {
            return;
        }
        mIsLoaderTaskRunning = true;
    }
    // Optimize for end-user experience: if the Launcher is up and // running with the
    // All Apps interface in the foreground, load All Apps first. Otherwise, load the
    // workspace first (default).
    keep_running: {
        //加载和绑定workspace数据
        if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
        loadAndBindWorkspace();

        if (mStopped) {
            break keep_running;
        }
        //等待线程空闲的时候再继续加载,避免ANR
        waitForIdle();

        // second step
        //加载和绑定all apps数据
        if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
        loadAndBindAllApps();
    }

    // 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;
        mHasLoaderCompletedOnce = true;
    }
}

分析该方法,可见加载Launcher数据主要执行两个步骤:

(1)loadAndBindWorkspace();

(2)loadAndBindAllApps();

那就按着这两个步骤,继续往下看:

loadAndBindWorkspace();

private void loadAndBindWorkspace() {
    //加载绑定桌面数据任务开始true
    mIsLoadingAndBindingWorkspace = true;

    // Load the workspace
    if (DEBUG_LOADERS) {
        Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
    }
    //接下来先判断桌面数据是否完全加载
    if (!mWorkspaceLoaded) {
        //false,加载桌面数据
        loadWorkspace();
        synchronized (LoaderTask.this) {
            if (mStopped) {
                //任务已经被停止,返回
                return;
            }
            //任务未停止
            mWorkspaceLoaded = true;
        }
    }
    //桌面数据加载完成,将数据绑定到桌面
    // Bind the workspace
    bindWorkspace(-1);
}

根据mWorkspaceLoader这个标识判断桌面数据是否加载完成,如果为true则执行bindWorkspace(-1)绑定桌面数据,如果为false,则先执行桌面数据的加载loadWorkspace(),再执行数据的绑定。

//Launcher桌面的数据主要包括来自Launcher数据库的各种表,
//loadWorkspace方法负责从这些数据库表中读取数据并转译为Launcher桌面项的数据结构
private void loadWorkspace() {
    final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;

    final Context context = mContext;
    final ContentResolver contentResolver = context.getContentResolver();
    //获取包管理服务
    final PackageManager manager = context.getPackageManager();
    final boolean isSafeMode = manager.isSafeMode();
    //获取Launcher定制的应用程序管理接口
    final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
    final boolean isSdCardReady = context.registerReceiver(null,
            new IntentFilter(StartupReceiver.SYSTEM_READY)) != null;
    //获取桌面的行列数
    LauncherAppState app = LauncherAppState.getInstance();
    InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
    int countX = (int) profile.numColumns;
    int countY = (int) profile.numRows;
    //根据输入的标识对数据进行预处理
    //判断标识是否为清空桌面
    if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
        Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
        //删除数据
        LauncherAppState.getLauncherProvider().deleteDatabase();
    }
    //判断是否为迁移Launcher2的快捷方式
    if ((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) != 0) {
        // append the user's Launcher2 shortcuts
        Launcher.addDumpLog(TAG, "loadWorkspace: migrating from launcher2", true);
        //迁移Launcher2的快捷方式
        LauncherAppState.getLauncherProvider().migrateLauncher2Shortcuts();
    } else {
        // Make sure the default workspace is loaded
        //确保默认数据得以加载
        Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false);
        //根据需要加载默认快捷方式
        LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
    }

    synchronized (sBgLock) {
        clearSBgDataStructures();
        //更新并获取缓存
        final HashMap<String, Integer> installingPkgs = PackageInstallerCompat
                .getInstance(mContext).updateAndGetActiveSessionCache();

        final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
        final ArrayList<Long> restoredRows = new ArrayList<Long>();
        //获取快捷方式的uri
        final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
        if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
        //根据获取的uri查询Favorites表数据
        final Cursor c = contentResolver.query(contentUri, null, null, null, null);

        // +1 for the hotseat (it can be larger than the workspace)
        // Load workspace in reverse order to ensure that latest items are loaded first (and
        // before any earlier duplicates)
        //准备桌面占用情况标识映射
        final LongArrayMap<ItemInfo[][]> occupied = new LongArrayMap<>();
    ……
        // Break early if we've stopped loading
        //加载任务已经被停止
        if (mStopped) {
            //从数据库中清理需要删除的数据项
            clearSBgDataStructures();
            return;
        }
        //从数据库中清理需要删除的数据项
        if (itemsToRemove.size() > 0) {
            // Remove dead items
            contentResolver.delete(LauncherSettings.Favorites.CONTENT_URI,
                    Utilities.createDbSelectionQuery(
                            LauncherSettings.Favorites._ID, itemsToRemove), null);
            if (DEBUG_LOADERS) {
                Log.d(TAG, "Removed = " + Utilities.createDbSelectionQuery(
                        LauncherSettings.Favorites._ID, itemsToRemove));
            }

            // Remove any empty folder
            for (long folderId : LauncherAppState.getLauncherProvider()
                    .deleteEmptyFolders()) {
                sBgWorkspaceItems.remove(sBgFolders.get(folderId));
                sBgFolders.remove(folderId);
                sBgItemsIdMap.remove(folderId);
            }
        }
        //设置恢复状态为已恢复
        if (restoredRows.size() > 0) {
            // Update restored items that no longer require special handling
            ContentValues values = new ContentValues();
            values.put(LauncherSettings.Favorites.RESTORED, 0);
            contentResolver.update(LauncherSettings.Favorites.CONTENT_URI, values,
                    Utilities.createDbSelectionQuery(
                            LauncherSettings.Favorites._ID, restoredRows), null);
        }
        //处理在装在sdcard上的应用程序
        if (!isSdCardReady && !sPendingPackages.isEmpty()) {
            context.registerReceiver(new AppsAvailabilityCheck(),
                    new IntentFilter(StartupReceiver.SYSTEM_READY),
                    null, sWorker);
        }
        //处理桌面数据
        sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));

        // Remove any empty screens
        ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
        for (ItemInfo item: sBgItemsIdMap) {
            long screenId = item.screenId;
            if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
                    unusedScreens.contains(screenId)) {
                unusedScreens.remove(screenId);
            }
        }

        // If there are any empty screens remove them, and update.
        if (unusedScreens.size() != 0) {
            sBgWorkspaceScreens.removeAll(unusedScreens);
            updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
        }

        if (DEBUG_LOADERS) {
            Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
            Log.d(TAG, "workspace layout: ");
            int nScreens = occupied.size();
            for (int y = 0; y < countY; y++) {
                String line = "";

                for (int i = 0; i < nScreens; i++) {
                    long screenId = occupied.keyAt(i);
                    if (screenId > 0) {
                        line += " | ";
                    }
                    ItemInfo[][] screen = occupied.valueAt(i);
                    for (int x = 0; x < countX; x++) {
                        if (x < screen.length && y < screen[x].length) {
                            line += (screen[x][y] != null) ? "#" : ".";
                        } else {
                            line += "!";
                        }
                    }
                }
                Log.d(TAG, "[ " + line + " ]");
            }
        }
    }
}

加载数据完成就可以绑定数据到桌面了。

private void bindWorkspace(int synchronizeBindPage) {
    final long t = SystemClock.uptimeMillis();
    Runnable r;

    // Don't use these two variables in any of the callback runnables.
    // Otherwise we hold a reference to them.
    //获取Launcher的接口
    final Callbacks oldCallbacks = mCallbacks.get();
    //判断Launcher的接口是否存在
    if (oldCallbacks == null) {
        // This launcher has exited and nobody bothered to tell us. Just bail.
        Log.w(TAG, "LoaderTask running with no launcher");
        return;
    }
    // Save a copy of all the bg-thread collections
    //桌面数据项列表
    ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
    //桌面小部件项数据列表
    ArrayList<LauncherAppWidgetInfo> appWidgets =
            new ArrayList<LauncherAppWidgetInfo>();
    //经过排序的桌面索引列表
    ArrayList<Long> orderedScreenIds = new ArrayList<Long>();

    final LongArrayMap<FolderInfo> folders;
    final LongArrayMap<ItemInfo> itemsIdMap;
    //或冲区数据复制
    synchronized (sBgLock) {
        workspaceItems.addAll(sBgWorkspaceItems);
        appWidgets.addAll(sBgAppWidgets);
        orderedScreenIds.addAll(sBgWorkspaceScreens);

        folders = sBgFolders.clone();
        itemsIdMap = sBgItemsIdMap.clone();
    }
    //获取launcher当前所处的页面索引
    final boolean isLoadingSynchronously =
            synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE;
    int currScreen = isLoadingSynchronously ? synchronizeBindPage :
        oldCallbacks.getCurrentWorkspaceScreen();
    if (currScreen >= orderedScreenIds.size()) {
        // There may be no workspace screens (just hotseat items and an empty page).
        currScreen = PagedView.INVALID_RESTORE_PAGE;
    }
    final int currentScreen = currScreen;
    final long currentScreenId = currentScreen < 0
            ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen);

    // Load all the items that are on the current page first (and in the process, unbind
    // all the existing workspace items before we call startBinding() below.
    unbindWorkspaceItemsOnMainThread();
    //分离当前页面与其他页面的数据并通知Launcher开始加载
    // Separate the items that are on the current screen, and all the other remaining items
    //分类数据区定义
    ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
    ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
    ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
            new ArrayList<LauncherAppWidgetInfo>();
    ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
            new ArrayList<LauncherAppWidgetInfo>();
    LongArrayMap<FolderInfo> currentFolders = new LongArrayMap<>();
    LongArrayMap<FolderInfo> otherFolders = new LongArrayMap<>();
    //分类桌面项、小部件、文件夹数据
    filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
            otherWorkspaceItems);
    filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
            otherAppWidgets);
    filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders,
            otherFolders);
    //对数据进行排序
    sortWorkspaceItemsSpatially(currentWorkspaceItems);
    sortWorkspaceItemsSpatially(otherWorkspaceItems);

    // Tell the workspace that we're about to start binding items
    //在主线程上执行通知Launcher绑定开始任务
    r = new Runnable() {
        public void run() {
            Callbacks callbacks = tryGetCallbacks(oldCallbacks);
            if (callbacks != null) {
                //绑定数据到launcher,launcher回调
                callbacks.startBinding();
            }
        }
    };
    runOnMainThread(r);

    bindWorkspaceScreens(oldCallbacks, orderedScreenIds);

    // Load items on the current page
    bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
            currentFolders, null);
    if (isLoadingSynchronously) {
        r = new Runnable() {
            public void run() {
                Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) {
                    callbacks.onPageBoundSynchronously(currentScreen);
                }
            }
        };
        runOnMainThread(r);
    }

    // Load all the remaining pages (if we are loading synchronously, we want to defer this
    // work until after the first render)
    synchronized (mDeferredBindRunnables) {
        mDeferredBindRunnables.clear();
    }
    bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
            (isLoadingSynchronously ? mDeferredBindRunnables : null));

    // Tell the workspace that we're done binding items
    //实现包含了结束绑定通知的任务
    r = new Runnable() {
        public void run() {
            Callbacks callbacks = tryGetCallbacks(oldCallbacks);
            if (callbacks != null) {
                callbacks.finishBindingItems();
            }

            // If we're profiling, ensure this is the last thing in the queue.
            if (DEBUG_LOADERS) {
                Log.d(TAG, "bound workspace in "
                    + (SystemClock.uptimeMillis()-t) + "ms");
            }
            //设置加载以及绑定结束标识
            mIsLoadingAndBindingWorkspace = false;
        }
    };
    if (isLoadingSynchronously) {
        synchronized (mDeferredBindRunnables) {
            mDeferredBindRunnables.add(r);
        }
    } else {
        //在主线程中执行该任务
        runOnMainThread(r);
    }
}

加载和绑定好桌面数据就可以加载和绑定AllApps数据了

//通过调用两个方法来完成应用程序的数据加载即绑定
private void loadAndBindAllApps() {
    if (DEBUG_LOADERS) {
        Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
    }
    //判断应用程序菜单是否已加载
    if (!mAllAppsLoaded) {
        //未加载,执行加载绑定过程
        loadAllApps();
        synchronized (LoaderTask.this) {
            if (mStopped) {
                return;
            }
        }
        updateIconCache();
        synchronized (LoaderTask.this) {
            if (mStopped) {
                return;
            }
            //设置已经加载标识
            mAllAppsLoaded = true;
        }
    } else {
        //已加载数据,只执行绑定过程
        onlyBindAllApps();
    }
}

跟桌面的数据加载绑定过程类似,也是根据mAllAppsLoaded这个标识来判断执行的是加载还是绑定。

private void loadAllApps() {
    final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;

    final Callbacks oldCallbacks = mCallbacks.get();
    if (oldCallbacks == null) {
        // This launcher has exited and nobody bothered to tell us. Just bail.
        Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
        return;
    }
    //Android系统提供了多账户的概念,不同的账户下可以使用的应用程序是不同的,因此Launcher需要注意这个细节,
    //在不同账户下处理不同的应用程序列表信息,所有在加载应用程序列表的时候需要获取当前设备上的所有账户。
    final List<UserHandleCompat> profiles = mUserManager.getUserProfiles();
    // Clear the list of apps
    //清除列表
    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;
        }
        //将应用程序信息加入缓存区
        // 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));
        }
        //按账户保存查询到的应用列表,ManagedProfileHeuristic工具将查询得到的数据分类保存到共享文件
        final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
        if (heuristic != null) {
            runAfterBindCompletes(new Runnable() {

                @Override
                public void run() {
                    heuristic.processUserApps(apps);
                }
            });
        }
    }
    ……
    //将需要加载的应用程序菜单中的数据完成分类后,接着将数据发送到Launcher中处理
    // 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) {

                if (!FolderPlugins.getInstance().isEnable()) {//(arrayForBind == null || arrayForBind.isEmpty()) && ){
                    callbacks.bindAllApplications(added);
                } else {
                    callbacks.bindAllApplications2Folder(added, arrayForBind);
                }
                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");
            }
        }
    });
    // Cleanup any data stored for a deleted user.
    ManagedProfileHeuristic.processAllUsers(profiles, mContext);

    loadAndBindWidgetsAndShortcuts(mApp.getContext(), tryGetCallbacks(oldCallbacks),
            true /* refresh */);
    if (DEBUG_LOADERS) {
        Log.d(TAG, "Icons processed in "
                + (SystemClock.uptimeMillis() - loadTime) + "ms");
    }
}
private void onlyBindAllApps() {
    final Callbacks oldCallbacks = mCallbacks.get();
    if (oldCallbacks == null) {
        // This launcher has exited and nobody bothered to tell us. Just bail.
        Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
        return;
    }

    // shallow copy
    @SuppressWarnings("unchecked")
    final ArrayList<AppInfo> list
            = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
    final WidgetsModel widgetList = mBgWidgetsModel.clone();
    Runnable r = new Runnable() {
        public void run() {
            final long t = SystemClock.uptimeMillis();
            final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
            if (callbacks != null) {
                callbacks.bindAllApplications(list);
                callbacks.bindAllPackages(widgetList);
            }
            if (DEBUG_LOADERS) {
                Log.d(TAG, "bound all " + list.size() + " apps from cache in "
                        + (SystemClock.uptimeMillis()-t) + "ms");
            }
        }
    };
    boolean isRunningOnMainThread = !(sWorkerThread.getThreadId() == Process.myTid());
    if (isRunningOnMainThread) {
        r.run();
    } else {
        mHandler.post(r);
    }
}
    原文作者:pnying
    原文地址: https://blog.csdn.net/qq_27215521/article/details/60868309
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞