【问题标题】:Different behavior of XmlPullParser.getInputEncoding() on API11+ and pre-API11 versions of AndroidXmlPullParser.getInputEncoding() 在 API11+ 和 API11 之前的 Android 版本上的不同行为
【发布时间】:2013-04-15 06:55:54
【问题描述】:

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

其他发现:

我发现有一种方法可以正确检测 API11+ 设备上的文件编码,使用 FileInputStream 而不是 FileReader,如下所示:

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 和 pre-API11 上的 FileInputStream:

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

但是用 XmlPullParser.getInputEncoding() 检查编码的正确方法是什么?为什么不同版本的 Android 行为会因我使用的哪一个而不同:FileInputStream 还是 FileReader?

【问题讨论】:

    标签: android xml encoding filereader fileinputstream


    【解决方案1】:

    经过更多的尝试和错误,我终于设法弄清楚发生了什么。所以尽管the documentation 说:

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

    任何一个选择都可以。本节中的示例通过 Xml.newPullParser() 使用 ExpatPullParser。

    现实情况是,在较旧的 API 上,例如 2.3.3 Xml.newPullParser() 返回 ExpatPullParser 对象。在 Ice Cream Sandwich 及更高版本上,它返回 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”。

    【讨论】:

      【解决方案2】:

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

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

      来自getInputEncoding() documentation

      如果 inputEncoding 为 null 并且解析器支持编码 检测特征,它必须返回检测到的编码

      还有:

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

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

      然后:

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

      所以在 11 之前,您可以尝试在调用 .getInputEncoding 之前先调用 .next()

      【讨论】:

      • 我尝试按照您的说明进行操作,但在 pre-API11 上调用 .next() 没有帮助,getInputEncoding() 不断返回null
      • 请看我对问题的回答。你打电话给.next() 是对的,但还有第二点:我应该使用XmlPullParserFactory.newPullParser() 而不是Xml.newPullParser()。当然,使用FileInputStream 而不是FileReader
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多