我正在为我的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 +版本(蜂窝,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。因此,现在我可以在代码中插入派生叉以在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表示:


  从历史上看,Android具有此接口的两种实现:
  通过XmlPullParserFactory.newPullParser()获得KXmlParser。
  通过Xml.newPullParser()获得ExpatPullParser。
  
  两种选择都可以。本节中的示例通过Xml.newPullParser()使用ExpatPullParser。


事实是,在较旧的API(例如2.3.3)上,Xml.newPullParser()返回ExpatPullParser对象。当放在冰淇淋三明治上时,它返回KXmlParser对象。从this blog post可以看出,Android开发者自2011年12月以来就知道这一点:


  在Ice Cream Sandwich中,我们将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”。

10-04 10:12