Android | StandardCharsets.UTF_8.toString() 遇到的兼容问题记录

在Android开发中,处理字符串编码和解码是非常常见的需求。标准库中的 java.nio.charset.StandardCharsets 提供了方便且安全的字符集常量,尤其是 StandardCharsets.UTF_8,被广泛用于网络通信、文件读写等场景。然而,在开发过程中,遇到了一个关于 StandardCharsets.UTF_8.toString() 的兼容性问题,该问题在Android 6.0及以下版本中会导致 URLEncoderURLDecoder 的编码解码出现异常。本文记录一下遇到的这个问题。

问题描述

在Android 6.0及以下版本中,调用 StandardCharsets.UTF_8.toString() 返回的并不是预期的 "UTF-8" 字符串,而是返回了类似于 java.nio.charset.CharsetICUUTF-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()的方式来优化。

相关推荐
Godikov1 小时前
硬件异步事件下的业务一致性:智能柜领取/归还流程的竞态设计
android
Godikov1 小时前
不用 Netty:Android 手写 TCP 客户端实战——自定义协议帧、粘包处理与可靠重连
android
事圆则缓1 小时前
Java 面向对象如何落到 Android View 体系
android
iPad协议个微协议2 小时前
# 微信自动营销系统如何基于 WechatApi 做事件驱动设计
android·微信·企业微信·个人开发·wechatapi·微信个人号开发
AirDroid_cn3 小时前
隐私沙盒:第三方应用读取剪切板内容如何拦截?
android·gitee
2501_915918415 小时前
怎么把 Python 写的 Flet 应用打包成 iOS App 并上架 App Store?
android·ios·小程序·https·uni-app·iphone·webview
邪修king6 小时前
Re:Linux系统篇(二十六):文件系统(二):Ext 文件系统底层详解:从 inode、块组到软硬链接,结合 Windows 讲透文件管理本质
android·java·linux
又见情义7 小时前
RK3568 Android 13 版本号管理实战:基于 ROCKCHIP_BUILD_NUMBER 的定制化方案
android
终端安全笔记7 小时前
iOS 27 强制 TLS 1.2:租赁设备的注册链路会在哪一环断
android·网络·安全·ios·智能手机
mmsx7 小时前
MapLibre 实战 11|用户说"我的地块丢了":一个 sealed class 图层模型,和四个让我重构三版的坑
android·前端·开源