XmlPullParser.getInputEncoding()对API11 +和pre-API11的Andr​​oid版本不同的行为不同、行为、版本、getInputEncoding

2023-09-07 01:46:59 作者:擦干眼泪我还是王

我开发我的Andr​​oid应用程序,使数据备份和恢复的新功能。我使用的XML文件来备份数据。这是一块code,设置编码的输出文件:

I am developing a new feature for my android app to enable data backup and restore. I am using XML files to backup data. This is a piece of code that sets encoding for an output file:

XmlSerializer serializer = Xml.newSerializer();
FileWriter fileWriter = new FileWriter(file, false);
serializer.setOutput(fileWriter);
serializer.startDocument("UTF-8", true);
[... Write data to the file....]

这是我尝试从XML文件导入数据。首先,我检查,如果编码是正确的:

This is how I try to import data from an XML file. First, I check if the encoding is correct:

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

在这里,我遇到了一个问题。这code正常工作在Android 2.3.3(包括设备和仿真器),编码是正确地检测为UTF-8。但在API11 +版本(蜂窝,ICS,JB)的异常。当我运行这在调试模式下我可以看到parser.getInputEncoding()返回。我检查了生产上的2.3.3及以后的版本实际的XML文件,它们有相同的标题:< XML版本=1.0编码=UTF-8独立='是'? > 。 为什么getInputEncoding()返回在API11空+?

And here I'm running into a problem. This code works fine on Android 2.3.3(both a device and an emulator), the encoding is correctly detected as "UTF-8". But on API11+ versions(Honeycomb, ICS, JB) the exception is thrown. When I run this in debug mode I can see that parser.getInputEncoding() returns null. I checked the actual XML files produced on 2.3.3 and later versions and they have exactly the same headers: <?xml version='1.0' encoding='UTF-8' standalone='yes' ?>. Why does getInputEncoding() return null on API11+?

其他调查结果:

我发现,有一种方法可以正确检测上API11 +设备文件编码采用的FileInputStream 而不是的FileReader 像这样的:

I have discovered that there is a way to correctly detect file encoding on API11+ devices using FileInputStream instead of FileReader like this:

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。所以现在我可以插入在code叉子上pre-API11使用的FileReader上API11 +和的FileInputStream:

In this case getInputEncoding() properly detects UTF-8 encoding on API11+ emulators and devices, but it returns null on 2.3.3. So for now I can insert a fork in code to use FileReader on API11+ and FileInputStream on pre-API11:

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

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

推荐答案

在一些试验和错误,我终于设法弄清是怎么回事。因此,尽管 说,文档:

After some more trial and error, I've finally managed to figure out what's going on. So despite the fact that the documentation says:

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

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

无论哪种选择是好的。本节中的示例使用ExpatPullParser,经由Xml.newPullParser()。

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

现实情况是,在旧的API,如2.3.3 Xml.newPullParser()返回 ExpatPullParser 目的。而在冰淇淋三明治和多达返回 KXmlParser 对象。正如我们可以从这个博客帖子 ,Android开发者知道这一点,因为2011年12月:

The reality is, that on older APIs, such as 2.3.3 Xml.newPullParser() returns ExpatPullParser object. While on Ice Cream Sandwich and up it returns KXmlParser object. And as we can see from this blog post, android developers knew about this since December 2011:

在冰淇淋三明治我们改变Xml.newPullParser()返回一个KxmlParser和删除我们的ExpatPullParser类。

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

......但从来没有困扰更新官方文档。

...but never bothered to update the official documentation.

那么,你如何检索冰淇淋三明治之前的API KXmlParser 对象?简单:

So how do you retrieve KXmlParser object on APIs before Ice Cream Sandwich? Simple:

XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser parser = factory.newPullParser();

...其实这适用于所有版本的Andr​​oid,新老的。然后您提供一个FileInputStream你的解析器的setInput()方法,使默认的编码

FileInputStream stream = null;
stream = new FileInputStream(file);
parser.setInput(stream, null);

在此,对原料药11或更高,你可以调用parser.getInputEncoding()的时候了,它会返回正确的编码。但在pre-API11版本中,除非你叫parser.next将返回null()首先,作为@Esailija正确地指出了他的答案。有趣的是,在API11 +调用next()没有任何负面影响无论如何,所以你可以放心的使用这种code上的所有版本:

After this, on APIs 11 and higher you can call parser.getInputEncoding() right away and it will return the correct encoding. But on pre-API11 versions, it will return null unless you call parser.next() first, as @Esailija correctly pointed out in his answer. Interestingly enough, on API11+ calling next() doesn't have any negative effect whatsoever, so you may safely use this code on all versions:

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

这将正确返回UTF-8。

And this will correctly return "UTF-8".