API11+ 和 API11 之前版本的 Android 上 XmlPullParser.getInputEncoding() 的不同行为

2023-12-01

我正在为我的 Android 应用程序开发一项新功能,以启用数据备份和恢复。我正在使用 XML 文件来备份数据。这是一段设置输出文件编码的代码:

XmlSerializer serializer = Xml.newSerializer();
FileWriter fileWriter = new FileWriter(file, false);
serializer.setOutput(fileWriter);
serializer.startDocument("UTF-8", true);
[... Write data to the file....]

这就是我尝试从 XML 文件导入数据的方法。首先,我检查编码是否正确:

XmlPullParser parser = Xml.newPullParser();
FileReader reader = new FileReader(file);
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(reader);
if(!"UTF-8".equals(parser.getInputEncoding())) {
    throw new IOException("Incorrect file encoding");
}
[... Read data from the file....]

我在这里遇到了一个问题。此代码在 Android 2.3.3(设备和模拟器)上运行良好,编码被正确检测为“UTF-8”。但在 API11+ 版本(Honeycomb、ICS、JB)上会引发异常。当我在调试模式下运行它时,我可以看到 parser.getInputEncoding() 返回null。我检查了 2.3.3 及更高版本上生成的实际 XML 文件,它们具有完全相同的标头:<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>. 为什么 getInputEncoding() 在 API11+ 上返回 null?

其他发现:

我发现有一种方法可以使用 API11+ 设备正确检测文件编码FileInputStream代替FileReader像这样:

XmlPullParser parser = Xml.newPullParser();
FileInputStream stream = new FileInputStream(file);
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(stream, null);
if(!"UTF-8".equals(parser.getInputEncoding())) {
    throw new IOException("Incorrect file encoding");
}
[... Read data from the file....]

在这种情况下,getInputEncoding() 在 API11+ 模拟器和设备上正确检测 UTF-8 编码,但在 2.3.3 上返回 null。因此,现在我可以在代码中插入一个分支,以在 API11+ 上使用 FileReader 并在 API11 之前的版本上使用 FileInputStream:

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    parser.setInput(stream, null);
} else {
    parser.setInput(reader);
}

但是使用 XmlPullParser.getInputEncoding() 检查编码的正确方法是什么?为什么不同版本的 Android 的行为会有所不同,具体取决于我使用的是 FileInputStream 还是 FileReader?


经过更多的尝试和错误,我终于弄清楚发生了什么事。所以尽管事实上文档 says:

历史上,Android 有两个该接口的实现: 通过 XmlPullParserFactory.newPullParser() 实现 KXmlParser。 ExpatPullParser,通过 Xml.newPullParser()。

任何一个选择都很好。本节中的示例通过 Xml.newPullParser() 使用 ExpatPullParser。

现实情况是,在较旧的 API 上,例如 2.3.3Xml.newPullParser()回报ExpatPullParser目的。当吃冰淇淋三明治时,它会返回KXmlParser目的。我们可以看到这篇博文,Android 开发者自 2011 年 12 月起就知道了这一点:

在 Ice Cream Sandwich 中,我们更改了 Xml.newPullParser() 以返回 KxmlParser 并删除了 ExpatPullParser 类。

...但从未费心更新官方文档。

那么如何检索KXmlParser在 Ice Cream Sandwich 之前就对 API 提出异议?简单的:

XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser parser = factory.newPullParser();

...事实上,这适用于所有版本的 Android,无论新旧。然后,您向解析器的 setInput() 方法提供 FileInputStream,保留默认编码null:

FileInputStream stream = null;
stream = new FileInputStream(file);
parser.setInput(stream, null);

此后,在 API 11 及更高版本上,您可以立即调用 parser.getInputEncoding() ,它将返回正确的编码。但在 API11 之前的版本中,除非您首先调用 parser.next() ,否则它将返回 null,正如 @Esailija 在他的答案中正确指出的那样。有趣的是,在 API11+ 上调用 next() 不会产生任何负面影响,因此您可以在所有版本上安全地使用此代码:

parser.next();
String encoding = parser.getInputEncoding();

这将正确返回“UTF-8”。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

API11+ 和 API11 之前版本的 Android 上 XmlPullParser.getInputEncoding() 的不同行为 的相关文章

随机推荐