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