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界面的过程了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值