根本原因是inputstreamreader未显式指定编码,导致系统默认utf-8解析gbk/gb2312或带bom的utf-8文件时解码错位,引发中文显示为问号、方块等乱码现象。

Android读XML时中文变问号或方块
根本原因是 InputStreamReader 没显式指定编码,系统默认用 UTF-8 解析,但 XML 文件实际是 GBK/GB2312 或带 BOM 的 UTF-8,导致字节流解码错位。
常见错误现象:DocumentBuilder.parse() 后文本节点显示为 、?、口,或日志里打印出乱码字符串;用 Log.d() 直接输出原始 InputStream 字节数组也能看到高位字节被截断或误判。
- 别依赖
new InputStreamReader(inputStream)的无参构造——它走Charset.defaultCharset(),在 Android 低版本(如 4.4)可能是 ISO-8859-1 - XML 声明里的
encoding="GBK"不会被InputStreamReader自动识别,必须手动传入 - 如果 XML 来自网络(如
HttpURLConnection.getInputStream()),响应头Content-Type: text/xml; charset=gb2312也仅作参考,仍需以实际字节为准
用 InputStreamReader 正确打开 XML 流
关键不是“选对编码”,而是“和源文件真实编码严格一致”。多数国产服务端返回的 XML 是 GBK,少数用 UTF-8(注意是否带 BOM)。
实操建议:
- 先用
FileInputStream或网络响应流读取前 4 字节,判断是否有 UTF-8 BOM(0xEF 0xBB 0xBF),有则用"UTF-8" - 没 BOM 且明确知道是 GBK(比如政府接口文档写了),就硬写
new InputStreamReader(inputStream, "GBK") - 不确定时,优先试
"UTF-8"和"GBK",用String.getBytes("UTF-8").length对比原始字节数,长度突变说明解码失败 - 避免用
"GB2312"——它不兼容部分汉字(如“镕”),"GBK"是超集,更稳妥
示例片段:
InputStream is = getXmlInputStream(); // 可能来自 assets 或网络 InputStreamReader reader = new InputStreamReader(is, "GBK"); // 不要省略第二个参数 InputSource source = new InputSource(reader); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(source);
DocumentBuilder.parse() 会忽略 InputStreamReader 编码吗?
不会忽略,但容易被误导:只要传的是 InputSource 且内部封装了正确编码的 InputStreamReader,解析器就会按该 Reader 的字符流读取,XML 声明里的 encoding 属性只用于校验,不改变已建立的字符流。
容易踩的坑:
- 误写成
new InputSource(inputStream)(没包 Reader),此时解析器自己 newInputStreamReader,又回到默认编码陷阱 - 把
InputStream多次读取(比如先用BufferedReader读一行看 encoding,再传给parse()),流已耗尽,报SAXParseException: Premature end of file - 用
OkHttpClient等库时,响应体response.body().byteStream()是一次性流,不能复用
assets 里放 XML 文件时的编码确认
Android Studio 默认用 UTF-8 保存 assets 文件,但 Windows 记事本另存时可能选 GBK,且不提示。乱码往往源于此。
验证和修复方法:
- 用命令行
file -i your.xml(Linux/macOS)或 VS Code 底部状态栏查看实际编码 - 在 AS 里右键 XML 文件 →
File Encoding→ 显式设为GBK并Convert,否则编辑时看着正常,打包后仍是乱码 - 如果必须用 GBK,建议改用
Resources.openRawResource()配合InputStreamReader,别用AssetManager.open()后直接丢给parse()
真正麻烦的从来不是代码怎么写,而是你手上的 XML 文件到底用什么编码存的——没确认这点,所有 InputStreamReader 参数都是蒙的。










