在Android开发中,处理字符串编码和解码是非常常见的需求。标准库中的 java.nio.charset.StandardCharsets
提供了方便且安全的字符集常量,尤其是 StandardCharsets.UTF_8
,被广泛用于网络通信、文件读写等场景。然而,在开发过程中,遇到了一个关于 StandardCharsets.UTF_8.toString()
的兼容性问题,该问题在Android 6.0及以下版本中会导致 URLEncoder
和 URLDecoder
的编码解码出现异常。本文记录一下遇到的这个问题。
问题描述
在Android 6.0及以下版本中,调用 StandardCharsets.UTF_8.toString()
返回的并不是预期的 "UTF-8" 字符串,而是返回了类似于 java.nio.charset.CharsetICU[UTF-8] 的字符串。这会导致 URLEncoder.encode()
和 URLDecoder.decode()
方法无法正确识别字符集,从而抛出 java.nio.charset.IllegalCharsetNameException
异常。
以下是一个典型的示例代码(注意下面是错误写法):
kotlin
import java.net.URLEncoder
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
val encoded = URLEncoder.encode("hello world!", StandardCharsets.UTF_8.toString())
val decoded = URLDecoder.decode(encoded, StandardCharsets.UTF_8.toString())
在Android 6.0及以下版本中运行上述代码会抛出以下异常:
ini
java.nio.charset.IllegalCharsetNameException: java.nio.charset.CharsetICU[UTF-8]
问题原因
导致这个问题的原因在于 StandardCharsets.UTF_8.toString()
的实现。在Android 6.0及以下版本中,toString()
方法返回了字符集对象的内部表示,而不是字符集名称。这在7.0版本及以上系统中得到了修复,toString()
方法被修改为调用 name()
方法,从而返回正确的字符集名称 "UTF-8"。
Android 6.0及以下版本的实现
来看下对应的Charset.java类:
Android 7.0及以上版本的实现
在7.0以上,toString()直接返回name()从而得到正确的字符集名称"UTF-8"。
java
/**
* Returns this charset's canonical name.
*
* @return The canonical name of this charset
*/
public final String name() {
return name;
}
解决方案
为了兼容Android 6.0及以下版本,需要避免使用 StandardCharsets.UTF_8.toString()
方法。可以直接使用字符串 "UTF-8"
或者直接调用StandardCharsets.UTF_8.name()
。这样可以确保在所有版本都能正确地进行编码和解码操作。
示例代码
以下是修改后的示例代码,直接使用 "UTF-8" 字符串:
kotlin
import java.net.URLEncoder
import java.net.URLDecoder
val encoded = URLEncoder.encode("hello world!", "UTF-8")
val decoded = URLDecoder.decode(encoded, "UTF-8")
或者是:
val encoded = URLEncoder.encode("hello world!", StandardCharsets.UTF_8.name())
val decoded = URLDecoder.decode(encoded, StandardCharsets.UTF_8.name())
完整解决方案
可以封装一个工具类来处理编码和解码操作,确保兼容性和可维护性。以下是一个完整的工具类示例:
kotlin
object EncodingUtil {
private const val CHARSET_UTF_8 = "UTF-8"
/**
* URL encode the given string using UTF-8 charset.
*/
fun urlEncode(value: String): String {
return URLEncoder.encode(value, CHARSET_UTF_8)
}
/**
* URL decode the given string using UTF-8 charset.
*/
fun urlDecode(value: String): String {
return URLDecoder.decode(value, CHARSET_UTF_8)
}
}
使用方式:
kotlin
val encoded = EncodingUtil.urlEncode("hello world!")
val decoded = EncodingUtil.urlDecode(encoded)
总结一下,StandardCharsets.UTF_8.toString()
在Android 6.0及以下版本中的兼容性问题可以通过直接使用"UTF-8"
字符串或者StandardCharsets.UTF_8.name()
的方式来优化。