在API11 +和API的pre-API11版本上的XmlPullParser.getInputEncoding()的不同行为

时间:2013-04-15 06:55:55

标签: android xml encoding filereader fileinputstream

我正在为我的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?

其他调查结果:

我发现有一种方法可以使用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。所以现在我可以在代码中插入一个fork,以便在API11 + API11 +和FileInputStream上使用FileReader:

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

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

2 个答案:

答案 0 :(得分:4)

经过一些试验和错误之后,我终于弄明白了发生了什么。尽管事实the documentation说:

  

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

     

两种选择都没问题。本节中的示例使用ExpatPullParser,通过Xml.newPullParser()。

现实情况是,在较旧的API上,例如2.3.3 Xml.newPullParser()返回ExpatPullParser对象。在冰淇淋三明治上,它返回KXmlParser对象。正如我们从this blog post中看到的那样,Android开发人员自2011年12月就知道了这一点:

  

在冰淇淋三明治中,我们更改了Xml.newPullParser()以返回KxmlParser并删除了我们的ExpatPullParser类。

...但从不打扰更新官方文档。

那么如何在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”。

答案 1 :(得分:0)

FileReader和其他读者没有检测到编码。他们只是使用平台默认编码,巧合可以是UTF-8。它与文件的实际编码无关。

在您阅读足够的文件以查看encoding属性之前,您无法检测XML文件编码。

来自getInputEncoding() documentation

  

如果inputEncoding为null 且解析器支持编码   检测功能,必须返回检测到的编码

  

如果调用了setInput(Reader),则返回null。

因此,pre 11似乎不支持使用setInput(is, null)启用的检测。我不知道在使用"UTF-8"时您是如何获得setInput(reader)的,因为文档说它应该返回null

然后:

  

首次调用next后,如果XML声明存在此方法   将返回声明的编码。

因此,在前11版中,您可以在致电.next()之前尝试致电.getInputEncoding