XmlPullParser.getInputEncoding()在Android的API11和AP...

codeday· 2019-10-23
本文来自 codeday ,作者 codeday
我正在为我的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文件,它们具有完全相同的标头:.为什么getInputEncoding()在API11上返回null?

其他发现:

我发现有一种方法可以使用FileInputStream而不是FileReader正确检测API11设备上的文件编码,如下所示:

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)而表现不同?

最佳答案
经过更多的试验和错误之后,我终于设法弄清楚了发生了什么.因此,尽管事实是the documentation表示:

Historically Android has had two implementations of this interface:
KXmlParser via XmlPullParserFactory.newPullParser().
ExpatPullParser, via Xml.newPullParser().

Either choice is fine. The example in this section uses ExpatPullParser, via Xml.newPullParser().

现实是,在较旧的API(例如2.3.3 Xml.newPullParser())上返回ExpatPullParser对象.在Ice Cream Sandwich上,它返回KXmlParser对象.从this blog post可以看出,Android开发者自2011年12月以来就知道这一点:

In Ice Cream Sandwich we changed Xml.newPullParser() to return a KxmlParser and deleted our ExpatPullParser class.

…但是从不费心去更新官方文档.

那么,如何在Ice Cream Sandwich之前在API上检索KXmlParser对象?简单:

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”.