如何在Android 9上保持UTF-8的向后兼容性?

Android 9中引入的行为变化之一是更严格的UTF-8解码器。如果我们有一个字节数组不是正确的 UTF-8 字符串(例如随机字节或一些二进制数据),并尝试从中创建一个字符串:


return new String(bytes)

Android将选择UTF-8作为首选编码(这很好),但在Android 9上返回的结果与在旧版本的Android上略有不同。


我知道将随机字节转换为UTF-8字符串听起来并不是一个好主意,但我现在需要向后兼容。


有没有一个选项可以在所有Android版本上获得完全相同的字符串结果?


编辑:


重现步骤:


    byte[] bytes =  new byte[]{25, 17, 113, 18, 62, 121, -6, -71, 45, -126, -113, 122, 58, 49, -30, -53, -66, -7, 0, -41};

    char[] password = new String(bytes).toCharArray();

    byte[] passKey = PBEParametersGenerator.PKCS12PasswordToBytes(password);

    Log.d("TEST", "Bytes: ".concat(Arrays.toString(passKey)));

安卓<9.0的输出:


[0, 25, 0, 17, 0, 113, 0, 18, 0, 62, 0, 121, -1, -3, 0, 45, -1, -3, -1, -3, 0, 122, 0, 58, 0, 49, -1, -3, 2, -2, -1, -3, 0, 0, -1, -3, 0, 0]

安卓9.0的输出:


[0, 25, 0, 17, 0, 113, 0, 18, 0, 62, 0, 121, -1, -3, -1, -3, 0, 45, -1, -3, -1, -3, 0, 122, 0, 58, 0, 49, -1, -3, 2, -2, -1, -3, 0, 0, -1, -3, 0, 0]



犯罪嫌疑人X
浏览 68回答 1
1回答

小唯快跑啊

您可以使用此代码,该代码是从以前 Android 版本的 UTF-8 解码器移植而来的。private static final char REPLACEMENT_CHAR = (char) 0xfffd;public static char[] byteArrayToCharArray(byte[] data) {&nbsp; &nbsp; char[] value;&nbsp; &nbsp; final int offset = 0;&nbsp; &nbsp; final int byteCount = data.length;&nbsp; &nbsp; char[] v = new char[byteCount];&nbsp; &nbsp; int idx = offset;&nbsp; &nbsp; int last = offset + byteCount;&nbsp; &nbsp; int s = 0;&nbsp; &nbsp; outer:&nbsp; &nbsp; while (idx < last) {&nbsp; &nbsp; &nbsp; &nbsp; byte b0 = data[idx++];&nbsp; &nbsp; &nbsp; &nbsp; if ((b0 & 0x80) == 0) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 0xxxxxxx&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Range:&nbsp; U-00000000 - U-0000007F&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int val = b0 & 0xff;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v[s++] = (char) val;&nbsp; &nbsp; &nbsp; &nbsp; } else if (((b0 & 0xe0) == 0xc0) || ((b0 & 0xf0) == 0xe0) ||&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ((b0 & 0xf8) == 0xf0) || ((b0 & 0xfc) == 0xf8) || ((b0 & 0xfe) == 0xfc)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int utfCount = 1;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ((b0 & 0xf0) == 0xe0) utfCount = 2;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else if ((b0 & 0xf8) == 0xf0) utfCount = 3;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else if ((b0 & 0xfc) == 0xf8) utfCount = 4;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else if ((b0 & 0xfe) == 0xfc) utfCount = 5;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 110xxxxx (10xxxxxx)+&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Range:&nbsp; U-00000080 - U-000007FF (count == 1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Range:&nbsp; U-00000800 - U-0000FFFF (count == 2)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Range:&nbsp; U-00010000 - U-001FFFFF (count == 3)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Range:&nbsp; U-00200000 - U-03FFFFFF (count == 4)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Range:&nbsp; U-04000000 - U-7FFFFFFF (count == 5)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (idx + utfCount > last) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v[s++] = REPLACEMENT_CHAR;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Extract usable bits from b0&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int val = b0 & (0x1f >> (utfCount - 1));&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i < utfCount; ++i) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; byte b = data[idx++];&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ((b & 0xc0) != 0x80) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v[s++] = REPLACEMENT_CHAR;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; idx--; // Put the input char back&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue outer;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Push new bits in from the right side&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val <<= 6;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val |= b & 0x3f;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Note: Java allows overlong char&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // specifications To disallow, check that val&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // is greater than or equal to the minimum&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // value for each count:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // count&nbsp; &nbsp; min value&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // -----&nbsp; &nbsp;----------&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //&nbsp; &nbsp;1&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;0x80&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //&nbsp; &nbsp;2&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 0x800&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //&nbsp; &nbsp;3&nbsp; &nbsp; &nbsp; &nbsp; 0x10000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //&nbsp; &nbsp;4&nbsp; &nbsp; &nbsp; &nbsp;0x200000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //&nbsp; &nbsp;5&nbsp; &nbsp; &nbsp; 0x4000000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Allow surrogate values (0xD800 - 0xDFFF) to&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // be specified using 3-byte UTF values only&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ((utfCount != 2) && (val >= 0xD800) && (val <= 0xDFFF)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v[s++] = REPLACEMENT_CHAR;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Reject chars greater than the Unicode maximum of U+10FFFF.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (val > 0x10FFFF) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v[s++] = REPLACEMENT_CHAR;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Encode chars from U+10000 up as surrogate pairs&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (val < 0x10000) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v[s++] = (char) val;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int x = val & 0xffff;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int u = (val >> 16) & 0x1f;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int w = (u - 1) & 0xffff;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int hi = 0xd800 | (w << 6) | (x >> 10);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int lo = 0xdc00 | (x & 0x3ff);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v[s++] = (char) hi;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v[s++] = (char) lo;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Illegal values 0x8*, 0x9*, 0xa*, 0xb*, 0xfd-0xff&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v[s++] = REPLACEMENT_CHAR;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; if (s == byteCount) {&nbsp; &nbsp; &nbsp; &nbsp; // We guessed right, so we can use our temporary array as-is.&nbsp; &nbsp; &nbsp; &nbsp; value = v;&nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; // Our temporary array was too big, so reallocate and copy.&nbsp; &nbsp; &nbsp; &nbsp; value = new char[s];&nbsp; &nbsp; &nbsp; &nbsp; System.arraycopy(v, 0, value, 0, s);&nbsp; &nbsp; }&nbsp; &nbsp; return value;}我通过创建一个20字节的随机数组,并与以前Android版本上的原始实现进行比较,然后重复了一百万次。我在多个较旧的Android版本上没有看到任何差异。new String(bytes).toCharArray()原始源代码来自这里:https://android.googlesource.com/platform/libcore/+/a7752f4d22097346dd7849b92b9f36d0a0a7a8f3/libdvm/src/main/java/java/lang/String.java#245 为了简化它,我删除了处理非UTF8字符集的部分,如果你使用,那么UTF-8是使用的默认字符集,所以你应该没问题。new String()此代码将为您提供所需的向后兼容性。但正如其他人会建议的那样,我建议如果可能的话,寻找一个更简单的解决方案,它不依赖于Android版本(或其他组件,这些组件不受您的控制)
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java