API11+;以及Android的API11之前版本

API11+;以及Android的API11之前版本,android,xml,encoding,filereader,fileinputstream,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); [...

我正在为我的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文件,它们的标题完全相同:
为什么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+上使用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而表现不同?

FileReader
而其他阅读器不会检测到编码。他们只是使用平台默认编码,碰巧可以是UTF-8。它与文件的实际编码无关

只有在充分阅读XML文件以查看
encoding
属性后,才能检测XML文件编码

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

以及:

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

因此,pre 11似乎不支持通过使用
setInput(is,null)
启用的检测。我不知道当使用
setInput(reader)
时,您如何得到
“UTF-8”
,因为文档中说它应该返回
null

然后:

在第一次调用next之后,如果存在XML声明,则使用此方法 将返回已声明的编码


因此,在11之前的版本中,您可以尝试在调用
.getInputEncoding
文件读取器
之前先调用
.next()
,其他读取器不会检测到编码。他们只是使用平台默认编码,碰巧可以是UTF-8。它与文件的实际编码无关

只有在充分阅读XML文件以查看
encoding
属性后,才能检测XML文件编码

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

以及:

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

因此,pre 11似乎不支持通过使用
setInput(is,null)
启用的检测。我不知道当使用
setInput(reader)
时,您如何得到
“UTF-8”
,因为文档中说它应该返回
null

然后:

在第一次调用next之后,如果存在XML声明,则使用此方法 将返回已声明的编码


因此,在11之前的版本中,您可以在调用
.getinpunecoding
之前先尝试调用
.next()
,经过更多的尝试和错误,我终于弄明白了发生了什么。因此,尽管事实表明:

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

两种选择都可以。本节中的示例通过Xml.newPullParser()使用ExpatPullParser

事实是,在旧的API上,如2.3.3
Xml.newPullParser()
返回
expapullparser
对象。在吃冰淇淋三明治时,它返回
KXmlParser
对象。从中我们可以看出,android开发者从2011年12月就知道了这一点:

在冰激凌三明治中,我们更改了Xml.newPullParser()以返回一个KxmlParser,并删除了我们的ExpatPullParser类

…但从未费心更新官方文件

那么,在吃冰淇淋三明治之前,如何在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);
在此之后,在API11和更高版本上,您可以立即调用parser.getInputEncoding(),它将返回正确的编码。但在API11之前的版本中,除非您首先调用parser.next(),否则它将返回null,正如@Esailija在他的回答中正确指出的那样。有趣的是,在API11+上调用next()不会产生任何负面影响,因此您可以在所有版本上安全地使用此代码:

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

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

经过更多的尝试和错误,我终于找到了发生的事情。因此,尽管事实表明:

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

两种选择都可以。本节中的示例通过Xml.newPullParser()使用ExpatPullParser

事实是,在旧的API上,如2.3.3
Xml.newPullParser()
返回
expapullparser
对象。在吃冰淇淋三明治时,它返回
KXmlParser
对象。我们可以看到