1. 程式人生 > >Launcher3原始碼分析(LauncherModel載入資料)

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

相關推薦

Launcher3原始碼分析(LauncherModel載入資料)

LauncherModel繼承BroadcastReceiver,顯然是一個廣播接收者。在上一篇Launcher的啟動中講到桌面資料的載入工作是在LauncherModel中執行的,那麼它是如何載入資料的呢?下面將分析Launcher和LauncherModel

Launcher3原始碼分析載入Workspace的資料 .

Launcher3的資料載入過程涉及到了兩個主要的類LauncherProvider和LauncherModel。 LauncherProvider LauncherProvider繼承自ContentProvider類,內部基於資料庫儲存實現了ContentProvid

從Android原始碼分析Activity載入流程

從Android原始碼分析Activity載入流程 概述:startActivity->startActivityForResult->Instrumentation::execStartActivity->scheduleLaunchActivi

Launcher3原始碼分析(Workspace)

Workspace主要功能:完成多個螢幕的以及桌布的顯示,多個螢幕之間的切換和桌布的新增。 /** * Used to inflate the Workspace from XML. */ public Workspac

Redis原始碼分析(一)——Redis資料結構-字串SDS

1. SDS簡介 Redis中使用的字串均為『簡單動態字串』(Simple Dynamic String),簡稱SDS。 SDS是在C字串的基礎上進行了一些包裝,使得它更符合Redis的使用場景。 在Redis中,C字串只用在一些無需修改的地方,如日誌列

【Netty原始碼分析】傳送資料過程

future.channel().writeAndFlush("Hello Netty Server ,I am a common client"); 呼叫AbstractChannel的writeAndFlush函式@Override public ChannelFutu

BeeGFS原始碼分析1-元資料服務概要分析

元資料服務是BeeGFS中用來維護檔案和目錄關係及其屬性配置的服務,其多執行緒epoll設計實現非常高效,主要流程如下: Con

1.Sentinel原始碼分析—FlowRuleManager載入規則做了什麼?

最近我很好奇在RPC中限流熔斷降級要怎麼做,hystrix已經1年多沒有更新了,感覺要被遺棄的感覺,那麼我就把眼光聚焦到了阿里的Sentinel,順便學習一下阿里的原始碼。 這一章我主要講的是FlowRuleManager在載入FlowRule的時候做了什麼,下一篇正式講Sentinel如何控制併發數的。 下

BeeGFS原始碼分析4-元資料同步

同步任務初始化 // fhgfs_meta\source\app\App.cpp void App::initComponent

Spring原始碼分析之-載入IOC容器

本文接上一篇文章 SpringIOC 原始碼,控制反轉前的處理(https://mp.weixin.qq.com/s/9RbVP2ZQVx9-vKngqndW1w) 繼續進行下面的分析 首先貼出 Spring bean容器的重新整理的核心 11個步驟進行祭拜(一定要讓我學會了...阿門) // 完成IoC容

Giraph 原始碼分析(五)—— 載入資料+同步總結

作者|白松 關於Giraph 共有九個章節,本文第五個章節。 環境:在單機上(機器名:giraphx)啟動了2個workers。

Flume NG原始碼分析(三)使用Event介面表示資料

Flume NG有4個主要的元件: Event表示在Flume各個Agent之間傳遞的資料流 Source表示從外部源接收Event資料流,然後傳遞給Channel Channel表示對從Source傳遞的Event資料流的臨時儲存 Sink表示從Channel中接收儲存的Event

兄弟連區塊鏈教程Fabric1.0原始碼分析ledgerID資料

1、idStore概述 Fabric支援建立多個Ledger,不同Ledger以ledgerID區分。 多個ledgerID及其創世區塊儲存在idStore資料庫中,idStore資料庫基於leveldb實現。 idStore預設使用路徑:/var/hyperledger/production

zigbee 之ZStack-2.5.1a原始碼分析(三)無線資料傳送和接收

前面說過SampleApp_Init和SampleApp_ProcessEvent是我們重點關注的函式,接下來分析無線傳送和接收相關的程式碼: 在SampleApp_ProcessEvent函式中: if ( events & SYS_EVENT_MSG ) {  &nbs

從SpringBoot原始碼分析 配置檔案的載入原理和優先順序

從SpringBoot原始碼分析 配置檔案的載入原理和優先順序 本文從SpringBoot原始碼分析 配置檔案的載入原理和配置檔案的優先順序     跟入原始碼之前,先提一個問題:   SpringBoot 既可以載入指定目錄下的配置檔案獲取配置項,也可

List去重(資料為物件的情況)及String中的equals()方法和hashCode()方法原始碼分析

面試中經常被問到的list如何去重,用來考察你對list資料結構,以及相關方法的掌握,體現你的java基礎學的是否牢固。 我們大家都知道,set集合的特點就是沒有重複的元素。如果集合中的資料型別是基本資料型別,可以直接將list集合轉換成set,就會自動去除重複的元素,這個就相對比較簡單。上一篇

資料之Spark(三)--- Spark核心API,Spark術語,Spark三級排程流程原始碼分析

一、Spark核心API ----------------------------------------------- [SparkContext] 連線到spark叢集,入口點. [HadoopRDD] extends RDD 讀取hadoop

自定義spring boot starter三部曲之三:原始碼分析spring.factories載入過程

本文是《自定義spring boot starter三部曲》系列的終篇,前文中我們開發了一個starter並做了驗證,發現關鍵點在於spring.factories的自動載入能力,讓應用只要依賴starter的jar包即可,今天我們來分析Spring和Spring boot原始碼,瞭解s

區塊鏈入門教程以太坊原始碼分析交易資料分析eth

交易的資料結構 交易的資料結構定義在core.types.transaction.go中,結構如下: type Transaction struct { data txdata // caches hash atomic.Value size atomic.Value from atomic.Value

Java原始碼分析——Class類、ClassLoader類解析(三) 類載入器、實現自定義類載入

    在這個系列的第一篇章就講解了Class類的獲取以及載入過程,但是並沒有提及具體的載入過程,在java中,載入一個類是通過ClassLoader類來執行的,也就是類載入器完成。java中所有的類,都必須載入進jvm中才能執行,這個載入的意思是