1. 程式人生 > >Launcher3原始碼分析 — 載入Workspace的資料 .

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

Launcher3的資料載入過程涉及到了兩個主要的類LauncherProvider和LauncherModel。

LauncherProvider
這裡寫圖片描述

LauncherProvider繼承自ContentProvider類,內部基於資料庫儲存實現了ContentProvider的CRUD介面,這個類主要用於更新資料庫的資料。LauncherProvider內部維護了兩張資料表,favorites(用於儲存workspace上的內容資訊)和workspaceScreens(用於儲存screen的排序和頁數的資訊)。

favorites表的建立語句:

db.execSQL("CREATE TABLE
favorites (" + "_id INTEGER PRIMARY KEY," + // id "title TEXT," + // 名字 "intent TEXT," + // intent的字串描述 "container INTEGER," + // 容器型別,desktop或者hotseat "
screen INTEGER," + // 所在的螢幕號 "cellX INTEGER," + // item左上方所在的格子x位置 "cellY INTEGER," + // item左上方所在的格子y位置 "spanX INTEGER," + // item橫向佔用的格子數 "
spanY INTEGER," + // item縱向佔用的格子數 "itemType INTEGER," + // item的型別,app,widget,folder "appWidgetId INTEGER NOT NULL DEFAULT -1," + // widget的id "isShortcut INTEGER," + // 是否是快捷方式 "iconType INTEGER," + ... "iconPackage TEXT," + "iconResource TEXT," + "icon BLOB," + "uri TEXT," + "displayMode INTEGER," + "appWidgetProvider TEXT," + "modified INTEGER NOT NULL DEFAULT 0" + ");
");

favorites用於儲存workspace上的內容資訊,它儲存了內容的型別和所在的位置等資訊。

workspaceScreens表的建立語句:

            db.execSQL("CREATE TABLE " + TABLE_WORKSPACE_SCREENS + " (" +
                    LauncherSettings.WorkspaceScreens._ID + " INTEGER," +
                    LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," +
                    LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" +
                    ");");

LauncherModel
當從資料庫獲取了資料後,需要一個數據物件在記憶體中維護這些資訊。Launcher3中定義了一系列的資料類,如下圖所示:
這裡寫圖片描述

  • AppInfo:代表所有應用介面的一個app
  • FolderInfo:代表一個資料夾
  • LauncherAppWidgetInfo:代表一個widget
  • ShortcutInfo:代表workspace和folder裡的app

LauncherModel用於維護記憶體中的資料,通過ContentResolver來更新LauncherProvider的資料,即資料庫的資料。LauncherModel內部有個繼承自Runnable的LoaderTask引用,Launcher的資料載入就是在這個類中執行,當呼叫LauncherModel.startLoader()時就會執行LoaderTask.run()方法,開始資料的載入工作。
LauncherModel內部維護了幾個列表,用於儲存從資料庫獲取的資料:

    // sBgItemsIdMap儲存了所有的ItemInfos (shortcuts, folders, and widgets),以item的id作為key
    static final HashMap<Long, ItemInfo> sBgItemsIdMap = new HashMap<Long, ItemInfo>();
    // sBgWorkspaceItems將作為引數傳遞給bindItems, 儲存了在workspace上顯示的所有app和folder資訊
    static final ArrayList<ItemInfo> sBgWorkspaceItems = new ArrayList<ItemInfo>();
    // sBgAppWidgets儲存了workspace上的widget資訊,作為引數傳遞給bindAppWidget()
    static final ArrayList<LauncherAppWidgetInfo> sBgAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
    // sBgFolders儲存了workspace上的folder資訊,作為引數傳遞給bindFolders()
    static final HashMap<Long, FolderInfo> sBgFolders = new HashMap<Long, FolderInfo>();
    // sBgDbIconCache儲存了在資料庫維護的item的圖片
    static final HashMap<Object, byte[]> sBgDbIconCache = new HashMap<Object, byte[]>();
    // sBgWorkspaceScreens按順序儲存了workspace上的螢幕
    static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();

LauncherModel的結構圖:
這裡寫圖片描述

Workspace資料的載入過程
Launcher3的資料載入分為兩部分,一個是Workspace頁面的資料:Launcher最主要的介面,使用者可自由編輯內容(widget,app, folder,screen),第二部分是所有應用介面。下面我們先來看Workspace的資料載入過程,通過在LoaderTask.run()方法裡呼叫loadAndBindWorkspace()開始整個過程,下面是具體程式碼:

        /** Returns whether this is an upgrade path */
        private boolean loadAndBindWorkspace() {
            mIsLoadingAndBindingWorkspace = true;

            boolean isUpgradePath = false;
            if (!mWorkspaceLoaded) {
                // Load the workspace
                isUpgradePath = loadWorkspace();
                synchronized (LoaderTask.this) {
                    if (mStopped) {
                        return isUpgradePath;
                    }
                    mWorkspaceLoaded = true;
                }
            }

            // Bind the workspace
            bindWorkspace(-1, isUpgradePath);
            return isUpgradePath;
        }

整個過程分為兩步,load和bind,完成第一步後會判斷任務是否終止,如果是則不繼續下一步。程式碼中用到了synchronized塊,因為這個過程涉及到多執行緒處理。我們先看loadWorkspace(),也就是workspace的資料載入過程,以下是大體流程:

這裡寫圖片描述

  1. 如果資料庫為空,先載入xml的資料
    載入時,首先會判斷資料庫的類容是否為空,如果是,會先從一個xml檔案中載入workspace的類容,Launcher3的預設xml檔案路徑為res/xml/default_workspace.xml,裡面定義了預設的app和widget資訊,這個過程由loadFavorates()實現。

預設xml的內容如下:

<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
    <!-- Far-left screen [0] -->

    <!-- Left screen [1] -->
    <appwidget
        launcher:packageName="com.android.settings"
        launcher:className="com.android.settings.widget.SettingsAppWidgetProvider"
        launcher:screen="1"
        launcher:x="0"
        launcher:y="3"
        launcher:spanX="4"
        launcher:spanY="1" />

    <!-- Right screen [3] -->
    <favorite
        launcher:packageName="com.android.gallery3d"
        launcher:className="com.android.gallery3d.app.Gallery"
        launcher:screen="3"
        launcher:x="1"
        launcher:y="3" />

    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
    <favorite
        launcher:packageName="com.android.dialer"
        launcher:className="com.android.dialer.DialtactsActivity"
        launcher:container="-101"
        launcher:screen="0"
        launcher:x="0"
        launcher:y="0" />
    <favorite
        launcher:packageName="com.android.contacts"
        launcher:className="com.android.contacts.activities.PeopleActivity"
        launcher:container="-101"
        launcher:screen="1"
        launcher:x="1"
        launcher:y="0" />
</favorites>

loadFavorates()程式碼如下:

       /**
         * Loads the default set of favorite packages from an xml file.
         *
         * @param db The database to write the values into
         * @param filterContainerId The specific container id of items to load
         */
        private int loadFavorites(SQLiteDatabase db, int workspaceResourceId) {
            Intent intent = new Intent(Intent.ACTION_MAIN, null);
            intent.addCategory(Intent.CATEGORY_LAUNCHER);
            ContentValues values = new ContentValues();

            PackageManager packageManager = mContext.getPackageManager();
            int i = 0;
            try {
                XmlResourceParser parser = mContext.getResources().getXml(workspaceResourceId);
                AttributeSet attrs = Xml.asAttributeSet(parser);
                beginDocument(parser, TAG_FAVORITES);

                final int depth = parser.getDepth();

                int type;
                while (((type = parser.next()) != XmlPullParser.END_TAG ||
                        parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {

                    if (type != XmlPullParser.START_TAG) {
                        continue;
                    }

                    boolean added = false;
                    final String name = parser.getName();

                    ......

                    // apps,widgets和folder都是Favorate型別
                    TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);

                    // 判斷元素是在workspace還是hotseat
                    long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
                    if (a.hasValue(R.styleable.Favorite_container)) {
                        container = Long.valueOf(a.getString(R.styleable.Favorite_container));
                    }

                    ......

                    if (TAG_FAVORITE.equals(name)) {
                        // 新增預設shortcut到資料庫
                        long id = addAppShortcut(db, values, a, packageManager, intent);
                        added = id >= 0;
                    } else if (TAG_APPWIDGET.equals(name)) {
                        // 新增預設widget到資料庫
                        added = addAppWidget(parser, attrs, type, db, values, a, packageManager);
                    } else if (TAG_FOLDER.equals(name)) {
                        // 新增folder到資料庫
                        added = addAppFolder();
                    }
                    if (added) i++;
                    a.recycle();
                }
            } catch (XmlPullParserException e) {
                Log.w(TAG, "Got exception parsing favorites.", e);
            } catch (IOException e) {
                Log.w(TAG, "Got exception parsing favorites.", e);
            } catch (RuntimeException e) {
                Log.w(TAG, "Got exception parsing favorites.", e);
            }

            // Update the max item id after we have loaded the database
            if (mMaxItemId == -1) {
                mMaxItemId = initializeMaxItemId(db);
            }

            return i;
        }

addAppShortcut,addAppWIdget,addAppFolder是把xml獲取到的資料insert到資料庫中,下面是addAppShortcut的程式碼:

        private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
                PackageManager packageManager, Intent intent) {
            long id = -1;
            ActivityInfo info;
            String packageName = a.getString(R.styleable.Favorite_packageName);
            String className = a.getString(R.styleable.Favorite_className);
            try {
                ComponentName cn;
                try {
                    cn = new ComponentName(packageName, className);
                    info = packageManager.getActivityInfo(cn, 0);
                } catch (PackageManager.NameNotFoundException nnfe) {
                    String[] packages = packageManager.currentToCanonicalPackageNames(
                        new String[] { packageName });
                    cn = new ComponentName(packages[0], className);
                    info = packageManager.getActivityInfo(cn, 0);
                }
                id = generateNewItemId();
                intent.setComponent(cn);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                        Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
                values.put(LauncherSettings.Favorites.INTENT, intent.toUri(0));
                values.put(LauncherSettings.Favorites.TITLE, info.loadLabel(packageManager).toString());
                values.put(LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.ITEM_TYPE_APPLICATION);
                values.put(LauncherSettings.Favorites.SPANX, 1);
                values.put(LauncherSettings.Favorites.SPANY, 1);
                values.put(LauncherSettings.Favorites._ID, generateNewItemId());
                // 將資料insert到資料庫
                if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
                    return -1;
                }
            } catch (PackageManager.NameNotFoundException e) {
                Log.w(TAG, "Unable to add favorite: " + packageName +
                        "/" + className, e);
            }
            return id;
        }

addAppWidget的程式碼:

 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
                int spanX, int spanY, Bundle extras) {
            boolean allocatedAppWidgets = false;
            final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);

            try {
                // 如果應用中需要嵌入widget(比如Launcher應用),需要有一個AppWidgetHost例項,
                // AppWidgetHost為每一個嵌入的widget分配一個id進行管理,AppWidgetHost為應用
                // 提供了與AppWidget service的互動,AppWidgetManager可以通過此id獲取provider資訊
                int appWidgetId = mAppWidgetHost.allocateAppWidgetId();

                values.put(LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET);
                values.put(LauncherSettings.Favorites.SPANX, spanX);
                values.put(LauncherSettings.Favorites.SPANY, spanY);
                values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
                values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
                values.put(LauncherSettings.Favorites._ID, generateNewItemId());
                dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);

                allocatedAppWidgets = true;

                // 每個新增到應用的widget都需要呼叫該方法
                appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn);

                // Send a broadcast to configure the widget
                if (extras != null && !extras.isEmpty()) {
                    Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
                    intent.setComponent(cn);
                    intent.putExtras(extras);
                    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
                    mContext.sendBroadcast(intent);
                }
            } catch (RuntimeException ex) {
                Log.e(TAG, "Problem allocating appWidgetId", ex);
            }

            return allocatedAppWidgets;
        }
  1. 將資料庫的資料載入到記憶體
/** Returns whether this is an upgradge path */
        private boolean loadWorkspace() {
            final Context context = mContext;
            final ContentResolver contentResolver = context.getContentResolver();
            final PackageManager manager = context.getPackageManager();
            final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
            final boolean isSafeMode = manager.isSafeMode();

            synchronized (sBgLock) {
                // 清空之前的記憶體資料(sBgWorkspaceItems,sBgAppWidgets等)
                clearSBgDataStructures();

                // 儲存無效資料的id,在後面統一從資料庫中刪掉
                final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
                // 查詢ContentProvider,返回favorites表的結果集
                final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
                final Cursor c = contentResolver.query(contentUri, null, null, null, null);

                // 用於判斷格子是否已經被佔用
                final HashMap<Long, ItemInfo[][]> occupied = new HashMap<Long, ItemInfo[][]>();

                try {
                    // 獲取資料庫每一列的索引值,通過cursor.getDataType(columnIndex)可獲取相應列的值
                    final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
                    final int intentIndex = c.getColumnIndexOrThrow
                            (LauncherSettings.Favorites.INTENT);
                    final int titleIndex = c.getColumnIndexOrThrow
                            (LauncherSettings.Favorites.TITLE);
                    final int iconTypeIndex = c.getColumnIndexOrThrow(
                            LauncherSettings.Favorites.ICON_TYPE);
                    final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
                    final int iconPackageIndex = c.getColumnIndexOrThrow(
                            LauncherSettings.Favorites.ICON_PACKAGE);
                    final int iconResourceIndex = c.getColumnIndexOrThrow(
                            LauncherSettings.Favorites.ICON_RESOURCE);
                    final int containerIndex = c.getColumnIndexOrThrow(
                            LauncherSettings.Favorites.CONTAINER);
                    final int itemTypeIndex = c.getColumnIndexOrThrow(
                            LauncherSettings.Favorites.ITEM_TYPE);
                    final int appWidgetIdIndex = c.getColumnIndexOrThrow(
                            LauncherSettings.Favorites.APPWIDGET_ID);
                    final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
                            LauncherSettings.Favorites.APPWIDGET_PROVIDER);
                    final int screenIndex = c.getColumnIndexOrThrow(
                            LauncherSettings.Favorites.SCREEN);
                    final int cellXIndex = c.getColumnIndexOrThrow
                            (LauncherSettings.Favorites.CELLX);
                    final int cellYIndex = c.getColumnIndexOrThrow
                            (LauncherSettings.Favorites.CELLY);
                    final int spanXIndex = c.getColumnIndexOrThrow
                            (LauncherSettings.Favorites.SPANX);
                    final int spanYIndex = c.getColumnIndexOrThrow(
                            LauncherSettings.Favorites.SPANY);
                    //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
                    //final int displayModeIndex = c.getColumnIndexOrThrow(
                    //        LauncherSettings.Favorites.DISPLAY_MODE);

                    // 儲存app資訊的物件引用
                    ShortcutInfo info;
                    String intentDescription;
                    // 儲存widget資訊的物件引用
                    LauncherAppWidgetInfo appWidgetInfo;
                    int container;
                    long id;
                    Intent intent;

                    // 遍歷結果集,如果當前是stop狀態,則停止遍歷
                    while (!mStopped && c.moveToNext()) {
                        // 標誌格子是否已被佔
                        AtomicBoolean deleteOnItemOverlap = new AtomicBoolean(false);
                        try {
                            // 獲取item的型別,app,widget,folder
                            int itemType = c.getInt(itemTypeIndex);

                            switch (itemType) {
                            // workspace上的app屬於ITEM_TYPE_APPLICATION
                            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                                id = c.getLong(idIndex);
                                intentDescription = c.getString(intentIndex);
                                try {
                                    intent = Intent.parseUri(intentDescription, 0);
                                    ComponentName cn = intent.getComponent();
                                    if (cn != null && !isValidPackageComponent(manager, cn)) {
                                        // 如果應用程式包名無效,則新增到移除列表
                                        itemsToRemove.add(id);
                                        continue;
                                    }
                                } catch (URISyntaxException e) {
                                    Launcher.addDumpLog(TAG, "Invalid uri: " + intentDescription, true);
                                    continue;
                                }

                                if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
                                    // app型別的shortcut,生成一個ShortcutInfo物件,對info的icon和title賦值
                                    info = getShortcutInfo(manager, intent, context, c, iconIndex,
                                            titleIndex, mLabelCache);
                                } else {
                                    // 不是app型別的shortcut
                                    info = getShortcutInfo(c, context, iconTypeIndex,
                                            iconPackageIndex, iconResourceIndex, iconIndex,
                                            titleIndex);

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

                                // 對info的其他資訊賦值
                                if (info != null) {
                                    info.id = id;
                                    info.intent = intent;
                                    container = c.getInt(containerIndex);
                                    info.container = container;
                                    info.screenId = c.getInt(screenIndex);
                                    info.cellX = c.getInt(cellXIndex);
                                    info.cellY = c.getInt(cellYIndex);
                                    info.spanX = 1;
                                    info.spanY = 1;
                                    // 跳過超出螢幕範圍的item
                                    if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
                                        if (checkItemDimensions(info)) {
                                            Launcher.addDumpLog(TAG, "Skipped loading out of bounds shortcut: "
                                                    + info + ", " + grid.numColumns + "x" + grid.numRows, true);
                                            continue;
                                        }
                                    }
                                    // check & update map of what's occupied
                                    deleteOnItemOverlap.set(false);
                                    if (!checkItemPlacement(occupied, info, deleteOnItemOverlap)) {
                                        // 格子已被佔用
                                        if (deleteOnItemOverlap.get()) {
                                            itemsToRemove.add(id);
                                        }
                                        break;
                                    }

                                    switch (container) {
                                    case LauncherSettings.Favorites.CONTAINER_DESKTOP:
                                    case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
                                        // 如果item是在workspace或者hotseat
                                        sBgWorkspaceItems.add(info);
                                        break;
                                    default:
                                        // 如果container是folder,則新增到相應的folder中
                                        FolderInfo folderInfo =
                                                findOrMakeFolder(sBgFolders, container);
                                        folderInfo.add(info);
                                        break;
                                    }
                                    // 將item新增到列表
                                    sBgItemsIdMap.put(info.id, info);

                                    // now that we've loaded everthing re-save it with the
                                    // icon in case it disappears somehow.
                                    queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
                                } else {
                                    throw new RuntimeException("Unexpected null ShortcutInfo");
                                }
                                break;

                            case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                                id = c.getLong(idIndex);
                                // 從sBgFolders獲取一個key為id的FolderInfo物件,如果找不到,
                                // 則new一個FolderInfo,並以此id為key新增到sBgFolders中
                                FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);

                                folderInfo.title = c.getString(titleIndex);
                                folderInfo.id = id;
                                container = c.getInt(containerIndex);
                                folderInfo.container = container;
                                folderInfo.screenId = c.getInt(screenIndex);
                                folderInfo.cellX = c.getInt(cellXIndex);
                                folderInfo.cellY = c.getInt(cellYIndex);
                                folderInfo.spanX = 1;
                                folderInfo.spanY = 1;

                                // 跳過超出邊界的item
                                if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
                                    if (checkItemDimensions(folderInfo)) {
                                        Log.d(TAG, "Skipped loading out of bounds folder");
                                        continue;
                                    }
                                }
                                // 移除格子被佔的item
                                deleteOnItemOverlap.set(false);
                                if (!checkItemPlacement(occupied, folderInfo,
                                        deleteOnItemOverlap)) {
                                    if (deleteOnItemOverlap.get()) {
                                        itemsToRemove.add(id);
                                    }
                                    break;
                                }

                                switch (container) {
                                    case LauncherSettings.Favorites.CONTAINER_DESKTOP:
                                    case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
                                        sBgWorkspaceItems.add(folderInfo);
                                        break;
                                }

                                sBgItemsIdMap.put(folderInfo.id, folderInfo);
                                sBgFolders.put(folderInfo.id, folderInfo);
                                break;

                            case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                                // Read all Launcher-specific widget details
                                int appWidgetId = c.getInt(appWidgetIdIndex);
                                String savedProvider = c.getString(appWidgetProviderIndex);

                                id = c.getLong(idIndex);

                                // widgets是AppWidgetManager的物件引用
                                final AppWidgetProviderInfo provider =
                                        widgets.getAppWidgetInfo(appWidgetId);

                                if (!isSafeMode && (provider == null || provider.provider == null ||
                                        provider.provider.getPackageName() == null)) {
                                    String log = "Deleting widget that isn't installed anymore: id="
                                        + id + " appWidgetId=" + appWidgetId;
                                    Log.e(TAG, log);
                                    Launcher.addDumpLog(TAG, log, false);
                                    itemsToRemove.add(id);
                                } else {
                                    appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
                                            provider.provider);
                                    appWidgetInfo.id = id;
                                    appWidgetInfo.screenId = c.getInt(screenIndex);
                                    appWidgetInfo.cellX = c.getInt(cellXIndex);
                                    appWidgetInfo.cellY = c.getInt(cellYIndex);
                                    appWidgetInfo.spanX = c.getInt(spanXIndex);
                                    appWidgetInfo.spanY = c.getInt(spanYIndex);
                                    // 根據widget內部的width,height和padding計算出widget在launcher中所佔的格子
                                    int[] minSpan = Launcher.getMinSpanForWidget(context, provider);
                                    appWidgetInfo.minSpanX = minSpan[0];
                                    appWidgetInfo.minSpanY = minSpan[1];

                                    container = c.getInt(containerIndex);
                                    // widget的container只能是workspace或者hotseat.
                                    if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
                                        container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
                                        Log.e(TAG, "Widget found where container != " +
                                            "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
                                        continue;
                                    }

                                    appWidgetInfo.container = c.getInt(containerIndex);
                                    // Skip loading items that are out of bounds
                                    if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
                                        if (checkItemDimensions(appWidgetInfo)) {
                                            Log.d(TAG, "Skipped loading out of bounds app widget");
                                            continue;
                                        }
                                    }
                                    // check & update map of what's occupied
                                    deleteOnItemOverlap.set(false);
                                    if (!checkItemPlacement(occupied, appWidgetInfo,
                                            deleteOnItemOverlap)) {
                                        if (deleteOnItemOverlap.get()) {
                                            itemsToRemove.add(id);
                                        }
                                        break;
                                    }
                                    String providerName = provider.provider.flattenToString();
                                    if (!providerName.equals(savedProvider)) {
                                        ContentValues values = new ContentValues();
                                        values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
                                                providerName);
                                        String where = BaseColumns._ID + "= ?";
                                        String[] args = {Integer.toString(c.getInt(idIndex))};
                                        contentResolver.update(contentUri, values, where, args);
                                    }
                                    sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
                                    sBgAppWidgets.add(appWidgetInfo);
                                }
                                break;
                            }
                        } catch (Exception e) {
                            Launcher.addDumpLog(TAG, "Desktop items loading interrupted: " + e, true);
                        }
                    }
                } finally {
                    // cursor用完後需要關閉
                    if (c != null) {
                        c.close();
                    }
                }

                // Break early if we've stopped loading
                if (mStopped) {
                    clearSBgDataStructures();
                    return false;
                }

                // 在ContentProvider中刪除無效的items
                if (itemsToRemove.size() > 0) {
                    ContentProviderClient client = contentResolver.acquireContentProviderClient(
                            LauncherSettings.Favorites.CONTENT_URI);
                    // Remove dead items
                    for (long id : itemsToRemove) {
                        if (DEBUG_LOADERS) {
                            Log.d(TAG, "Removed id = " + id);
                        }
                        // Don't notify content observers
                        try {
                            client.delete(LauncherSettings.Favorites.getContentUri(id, false),
                                    null, null);
                        } catch (RemoteException e) {
                            Log.w(TAG, "Could not remove id = " + id);
                        }
                    }
                }

                if (loadedOldDb) {
                    // 載入screen資料,獲取最大螢幕id
                    long maxScreenId = 0;
                    // If we're importing we use the old screen order.
                    for (ItemInfo item: sBgItemsIdMap.values()) {
                        long screenId = item.screenId;
                        if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
                                !sBgWorkspaceScreens.contains(screenId)) {
                            sBgWorkspaceScreens.add(screenId);
                            if (screenId > maxScreenId) {
                                maxScreenId = screenId;
                            }
                        }
                    }
                    Collections.sort(sBgWorkspaceScreens);

                    LauncherAppState.getLauncherProvider().updateMaxScreenId(maxScreenId);
                    updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);

                    // Update the max item id after we load an old db
                    long maxItemId = 0;
                    // If we're importing we use the old screen order.
                    for (ItemInfo item: sBgItemsIdMap.values()) {
                        maxItemId = Math.max(maxItemId, item.id);
                    }
                    LauncherAppState.getLauncherProvider().updateMaxItemId(maxItemId);
                } else {
                    TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(mContext);
                    for (Integer i : orderedScreens.keySet()) {
                        sBgWorkspaceScreens.add(orderedScreens.get(i));
                    }

                    // Remove any empty screens
                    ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
                    for (ItemInfo item: sBgItemsIdMap.values()) {
                        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);
                    }
                }
            }
            return loadedOldDb;
        }

經過以上步驟,資料庫中的資料就存到了LauncherModel相應的資料列表裡(sBgItemsIdMap,sBgWorkspaceItemsm,sBgAppWidgets,sBgFolders,sBgWorkspaceScreens),下一步就是將資料繫結到Launcher介面的過程了。