【源码剖析】Launcher 8.0 源码 (16) --- Launcher 启动流程 第六步之LoadWorkspace 第1小步(4)解析布局xml文件

本文深入探讨了Android Launcher 8.0的启动流程,重点在于LoadWorkspace的首步骤,即解析布局XML文件。通过`loadFavorites`和`parseLayout`方法,将XML布局解析并存储到数据库中。解析过程涉及`TagParser`,如AppShortcut和Widget,用于创建应用图标和小部件。解析完成后,数据插入到数据库,为Launcher的启动和桌面布局提供基础。

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

Launcher8.0启动流程的第六步中loadworkspace的第1小步是获取数据库,在没有布局时,需要读取手机上的布局xml文件,通过第三个操作我们创建了新的数据库并选取了xml文件那么如何解析该xml文件呢?

 

答案是loadFavorites方法

该方法把loader对应的xml布局文件放到数据库中。

源码如下:

int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
        ArrayList<Long> screenIds = new ArrayList<Long>();
        int count = loader.loadLayout(db, screenIds);

        Collections.sort(screenIds);
        int rank = 0;
        ContentValues values = new ContentValues();
        for (Long id : screenIds) {
            values.clear();
            values.put(LauncherSettings.WorkspaceScreens._ID, id);
            values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, rank);
            if (dbInsertAndCheck(this, db, WorkspaceScreens.TABLE_NAME, null, values) < 0) {
                throw new RuntimeException("Failed initialize screen table"
                        + "from default layout");
            }
            rank++;
        }
        return count;
    }
}

 

从代码直观看,获取了screen的id并通过dbInsertAndCheck()方法存入数据库。

 

在此之前,int count = loader.loadLayout(db, screenIds); 此方法完成了桌面图标的数据库写入,源码如下:

public int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds) {
        return parseLayout(mLayoutId, screenIds);
}

protected int parseLayout(int layoutId, ArrayList<Long> screenIds)
        throws XmlPullParserException, IOException {
    XmlResourceParser parser = mSourceRes.getXml(layoutId);
    beginDocument(parser, mRootTag);
    final int depth = parser.getDepth();
    int type;
    HashMap<String, TagParser> tagParserMap = getLayoutElementsMap();
    int count = 0;
    while (((type = parser.next()) != XmlPullParser.END_TAG ||
            parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
        if (type != XmlPullParser.START_TAG) {
            continue;
        }
        count += parseAndAddNode(parser, tagParserMap, screenIds);
    }
    return count;
}

 

关键代码parseLayout。 先找loader中的xml获取mSourceRes.getXml(layoutId) 布局参数,实际将光标放到布局xml的开端。

随后while循环,根据关键词依次读取,读取的方法是是parseAndAddNode(parser, tagParserMap, screenIds);

 

protected int parseAndAddNode(
        XmlResourceParser parser,
        HashMap<String, TagParser> tagParserMap,
        ArrayList<Long> screenIds)
                throws XmlPullParserException, IOException {

    if (TAG_INCLUDE.equals(parser.getName())) {
        final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
        if (resId != 0) {
            // recursively load some more favorites, why not?
            return parseLayout(resId, screenIds);
        } else {
            return 0;
        }
    }

    mValues.clear();
    parseContainerAndScreen(parser, mTemp);
    final long container = mTemp[0];
    final long screenId = mTemp[1];

    mValues.put(Favorites.CONTAINER, container);
    mValues.put(Favorites.SCREEN, screenId);

    mValues.put(Favorites.CELLX,
            convertToDistanceFromEnd(getAttributeValue(parser, ATTR_X), mColumnCount));
    mValues.put(Favorites.CELLY,
            convertToDistanceFromEnd(getAttributeValue(parser, ATTR_Y), mRowCount));

    TagParser tagParser = tagParserMap.get(parser.getName());
    if (tagParser == null) {
        if (LOGD) Log.d(TAG, "Ignoring unknown element tag: " + parser.getName());
        return 0;
    }
    long newElementId = tagParser.parseAndAdd(parser);
    if (newElementId >= 0) {
        // Keep track of the set of screens which need to be added to the db.
        if (!screenIds.contains(screenId) &&
                container == Favorites.CONTAINER_DESKTOP) {
            screenIds.add(screenId);
        }
        return 1;
    }
    return 0;
}

 

以上我们看到获取了CONTAINER SCREEN CELLX CELLY等通用关键词,随后调用方法

TagParser tagParser = tagParserMap.get(parser.getName());获取每个词条的图标类型,

类型有application shortcut widget folder等等。

 

long newElementId = tagParser.parseAndAdd(parser);

而后返回的结果是在数据库中生成的数据库词条的id号。Id号是依次列出的。

 

不同tagParser下的parseAndAdd方法:

 

appShortcut类型是图标,就是应用的图标最常用的。

而shortcut是快捷方式,通常是应用往桌面上放置的快速按钮,比如快速拨号,具体某个人的通讯录,直接打开某个网页的链接等等。

主要关键词有packagename,intent等,下面以appshortcut为例。

 

protected class AppShortcutParser implements TagParser {
    @Override
    public long parseAndAdd(XmlResourceParser parser) {
        final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
        final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
        if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) {
            ActivityInfo info;
            try {
                ComponentName cn;
                try {
                    cn = new ComponentName(packageName, className);
                    info = mPackageManager.getActivityInfo(cn, 0);
                } catch (PackageManager.NameNotFoundException nnfe) {
              String[] packages = mPackageManager.currentToCanonicalPackageNames(
                            new String[] { packageName });
                    cn = new ComponentName(packages[0], className);
                    info = mPackageManager.getActivityInfo(cn, 0);
                }
                final Intent intent = new Intent(Intent.ACTION_MAIN, null)
                    .addCategory(Intent.CATEGORY_LAUNCHER)
                    .setComponent(cn)
                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
                return addShortcut(info.loadLabel(mPackageManager).toString(),
                        intent, Favorites.ITEM_TYPE_APPLICATION);
            } catch (PackageManager.NameNotFoundException e) {
                Log.e(TAG, "Favorite not found: " + packageName + "/" + className);
            }
            return -1;
        } else {
            return invalidPackageOrClass(parser);
        }
    }

 

最后会return addShortcut(info.loadLabel(mPackageManager).toString(),
                        intent, Favorites.ITEM_TYPE_APPLICATION);  把数据统一放到mValues而后insert到数据库里面。

 

protected long addShortcut(String title, Intent intent, int type) {
    long id = mCallback.generateNewItemId();
    mValues.put(Favorites.INTENT, intent.toUri(0));
    mValues.put(Favorites.TITLE, title);
    mValues.put(Favorites.ITEM_TYPE, type);
    mValues.put(Favorites.SPANX, 1);
    mValues.put(Favorites.SPANY, 1);
    mValues.put(Favorites._ID, id);
    if (mCallback.insertAndCheck(mDb, mValues) < 0) {
        return -1;
    } else {
        return id;
    }
}

 

再比如widget 增加了SPANX和SPANY 。 应用图标的话只占一格,而widget往往占多个格子。

 

protected class PendingWidgetParser implements TagParser {
    @Override
    public long parseAndAdd(XmlResourceParser parser)
            throws XmlPullParserException, IOException {
        final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
        final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
        mValues.put(Favorites.SPANX, getAttributeValue(parser, ATTR_SPAN_X));
        mValues.put(Favorites.SPANY, getAttributeValue(parser, ATTR_SPAN_Y));
        mValues.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
        Bundle extras = new Bundle();
        int widgetDepth = parser.getDepth();
        int type;
        while ((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > widgetDepth) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }
        }
        return verifyAndInsert(new ComponentName(packageName, className), extras);
    }

 

 

至此,完成了loadWorkspace()的第一小步:第一个操作call LauncherProvider的方法。 call方法进行判断并记录下数据库是否为空。接着调用loadDefaultFavoritesIfNecessary方法,该方法在数据库为空时才会运行。 第二个操作读取布局的xml文件,按照特殊字符,特殊广播,特殊程序,本地布局。接着第三个操作,获取能读取到的第一个布局。最后第四个操作依次读取布局里面的内容存储到数据库里面。

 

如果不是第一次开机,那么数据库里面存储着布局的信息,第一小步是不做的,直接做第二小步获取数据库的信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值