Engine的WDataManager 是如何解析config.xml文件的

这篇文章简单学习了混合开发框架Appcan的Engine是如何解析widget的配置文件config.xml。文中的源码参考官方开源项目appcan-android

首先, 在application类中调用Appcan.classinitSync(context)方法进行同步初始化,在该方法中判断assets目录中是否有rootWidget(所谓的rootWidget就是文件夹名字为widget的widget),判断的标准就是widget目录中是否含有config.xml文件。如果有则调用getWidgetData() 解析config.xml文件,否则获取默认的widget,默认的widget就是new了一个WWidgetData对象, 然后为该对象的 m_appId、m_indexUrl属性赋上默认值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    if (wDataManager.isHasAssetsWidget()) {
mWidgetData = wDataManager.getWidgetData();
} else {
mWidgetData = wDataManager.getDefaultWidgetData();
}

/**
* 获取默认的widget
* @return
*/
public WWidgetData getDefaultWidgetData() {
WWidgetData widgetData = new WWidgetData();
widgetData.m_appId = "default";
widgetData.m_indexUrl = "index.html";
return widgetData;
}

重点分析getWidgetData() 和 getWidgetDataByXML(path,type).

getWidgetDataByXML(path,type)的作用就是解析config.xml ,解析完成后返回一个WWidgetData对象.

getWidgetData()

该方法会首先调用 getWidgetDataByXML(path,type) ,解析路径 assets目录中文件夹名字为widget的微应用,该widget就是rootWidget.

1
2
3
//m_rootWidgetConfigPath为 widget/config.xml
WWidgetData assetsData = getWidgetDataByXML(m_rootWidgetConfigPath, 0)
isUpdateWidget = checkAppStatus(m_context, assetsData.m_appId)

然后调用checkAppStatus(context, appid)来判断是widget是否需要更新,如果isUpdateWidget为true, 则开启一个线程复制asssets目录中的rootwidget到 /data/data/files/widget/中.

heckAppStatus是如何判断的,这点自己还不是很理解,但是需要注意一点:appstatus 属性是在 values文件的Strings.xml中进行配置,appkey也是在该文件中配置的.

1
<string name="appstatus" translatable="false">6E4E99740100CFB7447DAB9CCC55DD75F7D658</string>

接下来 如果有增量更新包,且其版本号大于当前APK的版本号,则进行同步拷贝操作,防止再次弹出增量更新提示框,否则,才进行异步拷贝操作. 这里的逻辑比较复杂,暂时不做研究.

getWidgetDataByXML(path,type)

这个方法比较重要,主要解析config.xml配置文件,返回一个 WWidgetData 对象.

1. 解析config.xml文件返回widgetData对象

首先判断path是否是以”/“开头,

  • 如果是,表示传入的是一个普通的路径,创建一个file对象,然后调用getWidgetDataOfXML(inputStream)
  • 如果不是,则表示是在assets目录中,则根据assets中的路径,调用getWidgetDataOfXML(inputStream)

这两种情况最终调用的都是getWidgetDataOfXML(inputStream)来解析config.xml文件的。

1
2
3
4
5
6
7
8
if (!path.startsWith("/")) {
widgetData = getWidgetDataOfXML(m_context.getAssets()
.open(path));
} else {
File file = new File(path);
inputStream = new FileInputStream(file);
widgetData = getWidgetDataOfXML(inputStream);
}

2. 解析 widgetPath 属性

当path 以 “/“开头说明该widget不是assets中的widget。

如果不是以”/“开头,则说明是一个assets widget, 如果此时type为3的话是一个普通的assets widget, 否则则是一个rootwidget

1
2
3
4
5
6
7
8
9
10
if (!path.startsWith("/")) {
if (type == 3) {
widgetPath = BUtility.F_ASSET_PATH + path.substring(0, path.lastIndexOf('/') + 1);
} else {
widgetPath = BUtility.F_ASSET_PATH + F_ROOT_WIDGET_PATH;
}
} else {
File file = new File(path);
widgetPath = BUtility.F_FILE_SCHEMA + file.getParentFile().getAbsolutePath() + "/";
}
  • 如果是以 “/“开头,则widgetPath如下:

    1
    widgetPath = file://  + file.getParentFile().getAbsolutePath() + "/";
  • 如果不是以 “/“开头,则说明该widget是assets中的widget,然后根据type判断是否是rootWidget

    • type 为3,widgetPath为如下内容,说明该widget是一个普通的widget

      1
      widgetPath = file:///android_asset/ + path.substring(0, path.lastIndexOf('/') + 1);
    • type 不为3,说明该widget是一个rootwidget

      1
      widgetPath = file:///android_asset/ + widget/ ;

3. 解析m_indexUrl 和m_iconPath 属性

如果 m_indexUrl 为”#” 或者为空时,m_indexUrl 默认为 widgetPath + “index.html”,也就是widget默认打开的首页为widget的根目录中的index.html页面。

否则m_indexUrl值为:将config.xml 解析得到的 m_indexUrl(config.xml中配置的src 属性的值)和widgetPath拼接后得到新的 widgetData.m_indexUrl属性值,也就是打开的首页为config.xml 中 配置的首页。

iconPath 的解析和 indexUrl的解析过程一样。

1
2
3
4
5
6
7
8
9
10
11
12
if ("#".equals(widgetData.m_indexUrl)
|| widgetData.m_indexUrl == null
|| widgetData.m_indexUrl.length() == 0) {
widgetData.m_indexUrl = widgetPath + "index.html";
} else {
if (!BUtility.uriHasSchema(widgetData.m_indexUrl)) {
widgetData.m_indexUrl = widgetPath + widgetData.m_indexUrl;
}
}
if (widgetData.m_iconPath != null && !BUtility.uriHasSchema(widgetData.m_iconPath)) {
widgetData.m_iconPath = widgetPath + widgetData.m_iconPath;
}

4. 设置 m_widgetPath属性的值

根据上边解析的widgetPath 和传入的type的值来设置widgetData.m_widgetPath的值.此处也要注意,

1
2
3
4
5
6
//type不为3 表示的是一个rootWidget
if (type == 3) {
widgetData.m_widgetPath = widgetPath .substring(BUtility.F_FILE_SCHEMA.length());
} else {
widgetData.m_widgetPath = getWidgetPath(type, widgetData.m_appId);
}

type为3的话,说明是rootWidget, 将widgetPath的协议头 file://去掉,此时 m_widgetPath 的格式为/android_asset/ + path,
如果type不为3, 则调用getWidgetPath(type, widgetData.m_appId),返回一个路径。

4.1 解析rootWidget的 widgetPath

getWidgetPath(int type, String appId)是根据type的值用来解析普通widget的widgetPath

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

private String getWidgetPath(int type, String appId) {
String appPath = null;
if (type == 0) {
appPath = BUtility.F_APP_PATH; // F_APP_PATH:widgetone/apps/
} else if (type == 1) {
appPath = BUtility.F_APP_PATH;
} else {
appPath = BUtility.F_WIDGET_PATH; // F_WIDGET_PATH: widgetone/widgets/
}
String appIdPath = null;
if (WDataManager.F_SPACE_APPID.equals(appId)) { // F_SPACE_APPID: 9999999
appIdPath = F_SPACE_APPID;
} else {
appIdPath = appId;
}
}

如上代码:

首先解析appPath的值,如果type为0或1, appPath为 ` widgetone/apps/`,否则appPath为 `widgetone/widgets/`。

然后解析appIdPath的值, 如果appId为 "9999999" , 则appIdPath为 "9999999",否则appidPath为 appid 。

然后根据 isWidgetOneSBox 判断是否是沙箱路径,

如果是沙箱环境,则根路径为 sd卡的根路径拼接上appPath和appidPath,

否则根路径为`/data/data/packageName/files/`拼接上appPath和appidPath。

如果是则该路径, type =1 ,则在根路径的基础上拼接 `myspace`

总结

解析config.xml的步骤大致总结如下:

AppCan.java 的初始化方法initSync中首先判断assets目录中是有名字为wdiget的文件夹,如果有则说明存在rootWidget,然后解析config.xml文件;否则不存在rootwidget, 创建一个 wDataManager对象,为m_appId和m_indexUrl赋默认值。

然后调用getWidgetDataByXML(String path, int type)解析config.xml。

在该方法中首先调用 getWidgetDataOfXMLconfig.xml解析。

然后解析widgetPath属性。

判断根据`path`是否是以"/"开头
    如果是,则说明该widget不是assets中的widget, 然后拼接`file://`前缀.
    如果不是,则拼接`file:///android_asset/`。
        然后判断type是否为3,如果为3则说明是rootWidget。
        如果不为3,则说明是assets中的普通的widget。

接着,根据上一步解析的widgetPath来拼接m_indexUrl 和m_iconPath 属性。

最后在根据解析的 widgetPath 属性来给 widgetData的 m_wgtType 属性赋值。

根据type是否为3来为 m_wgtType 属性复制
    如果为3, 则说明是一个rootWidget, 则将 widgetPath 前边的 file:// 去掉,最终格式为`/android_asset/ + path`
    如果不为3 ,首先会根据type的值(0/1/其它)来设置appPath,根据appId来设置appIdPath的值。 然后将根路径拼接上appPath和appidPath,得到最终的值。
    (沙箱环境的根路径为/data/data/packageName/files/`,非沙箱环境的根路径为sd卡的根路径)。

可以看到,如果是type为3,则说明是rootWidget,该rootWidget存放在assets目录中的widget文件夹中。 m_wgtType 是以"file:///android_asset/" 开头的,
 如果type不为3,则会根据是否是沙箱环境设置不同的路径。

博客编号: 26