¿Cómo mantener la compatibilidad hacia atrás de UTF-8 en Android 9?

LR89:

uno de los cambios de comportamiento introducidas en Android 9 es más estricta UTF-8 decodificador . Si tenemos una matriz de bytes que no son correctas cadena UTF-8 (por ejemplo bytes aleatorios o algunos datos binarios) y tratar de crear una cadena de ella:

return new String(bytes)

el Android escogerá UTF-8 como codificación preferida (que está bien) pero devolver un resultado ligeramente diferente en Android 9 que en las versiones anteriores de Android.

Yo sé que la transformación de bytes aleatorios en cadena UTF-8 no suena como una buena idea en un primer lugar, pero necesito una compatibilidad hacia atrás por ahora.

¿Hay una opción para obtener exactamente el mismo resultado de cuerdas en todas las versiones de Android?

Editar:

Pasos para reproducir:

    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)));

Salida para Android <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]

Salida para Android 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]
Daniel Zolnai :

Puede utilizar este código, que fue portado desde el UTF-8 decodificador de versiones anteriores de Android.

private static final char REPLACEMENT_CHAR = (char) 0xfffd;

public static char[] byteArrayToCharArray(byte[] data) {
    char[] value;
    final int offset = 0;
    final int byteCount = data.length;

    char[] v = new char[byteCount];
    int idx = offset;
    int last = offset + byteCount;
    int s = 0;
    outer:
    while (idx < last) {
        byte b0 = data[idx++];
        if ((b0 & 0x80) == 0) {
            // 0xxxxxxx
            // Range:  U-00000000 - U-0000007F
            int val = b0 & 0xff;
            v[s++] = (char) val;
        } else if (((b0 & 0xe0) == 0xc0) || ((b0 & 0xf0) == 0xe0) ||
                ((b0 & 0xf8) == 0xf0) || ((b0 & 0xfc) == 0xf8) || ((b0 & 0xfe) == 0xfc)) {
            int utfCount = 1;
            if ((b0 & 0xf0) == 0xe0) utfCount = 2;
            else if ((b0 & 0xf8) == 0xf0) utfCount = 3;
            else if ((b0 & 0xfc) == 0xf8) utfCount = 4;
            else if ((b0 & 0xfe) == 0xfc) utfCount = 5;
            // 110xxxxx (10xxxxxx)+
            // Range:  U-00000080 - U-000007FF (count == 1)
            // Range:  U-00000800 - U-0000FFFF (count == 2)
            // Range:  U-00010000 - U-001FFFFF (count == 3)
            // Range:  U-00200000 - U-03FFFFFF (count == 4)
            // Range:  U-04000000 - U-7FFFFFFF (count == 5)
            if (idx + utfCount > last) {
                v[s++] = REPLACEMENT_CHAR;
                continue;
            }
            // Extract usable bits from b0
            int val = b0 & (0x1f >> (utfCount - 1));
            for (int i = 0; i < utfCount; ++i) {
                byte b = data[idx++];
                if ((b & 0xc0) != 0x80) {
                    v[s++] = REPLACEMENT_CHAR;
                    idx--; // Put the input char back
                    continue outer;
                }
                // Push new bits in from the right side
                val <<= 6;
                val |= b & 0x3f;
            }
            // Note: Java allows overlong char
            // specifications To disallow, check that val
            // is greater than or equal to the minimum
            // value for each count:
            //
            // count    min value
            // -----   ----------
            //   1           0x80
            //   2          0x800
            //   3        0x10000
            //   4       0x200000
            //   5      0x4000000
            // Allow surrogate values (0xD800 - 0xDFFF) to
            // be specified using 3-byte UTF values only
            if ((utfCount != 2) && (val >= 0xD800) && (val <= 0xDFFF)) {
                v[s++] = REPLACEMENT_CHAR;
                continue;
            }
            // Reject chars greater than the Unicode maximum of U+10FFFF.
            if (val > 0x10FFFF) {
                v[s++] = REPLACEMENT_CHAR;
                continue;
            }
            // Encode chars from U+10000 up as surrogate pairs
            if (val < 0x10000) {
                v[s++] = (char) val;
            } else {
                int x = val & 0xffff;
                int u = (val >> 16) & 0x1f;
                int w = (u - 1) & 0xffff;
                int hi = 0xd800 | (w << 6) | (x >> 10);
                int lo = 0xdc00 | (x & 0x3ff);
                v[s++] = (char) hi;
                v[s++] = (char) lo;
            }
        } else {
            // Illegal values 0x8*, 0x9*, 0xa*, 0xb*, 0xfd-0xff
            v[s++] = REPLACEMENT_CHAR;
        }
    }
    if (s == byteCount) {
        // We guessed right, so we can use our temporary array as-is.
        value = v;
    } else {
        // Our temporary array was too big, so reallocate and copy.
        value = new char[s];
        System.arraycopy(v, 0, value, 0, s);
    }
    return value;
}

Yo he probado la unidad mediante la creación de una matriz aleatoria de 20 bytes, y comparando con el original new String(bytes).toCharArray()aplicación en la versión anterior de Android, y luego repetí un millón de veces. No he visto ninguna diferencia en múltiples versiones anteriores de Android.

El código fuente original es de aquí: https://android.googlesource.com/platform/libcore/+/a7752f4d22097346dd7849b92b9f36d0a0a7a8f3/libdvm/src/main/java/java/lang/String.java#245 Para simplificarlo, quité la parte que se ocupa de los conjuntos de caracteres no-UTF8, si se utiliza new String(), a continuación, es el juego de caracteres UTF-8 por defecto que se utiliza, por lo que debe estar bien.

Este código le conseguirá la compatibilidad hacia atrás, lo que quisieras. Pero a medida que otras personas le sugerirán que, aconsejo a buscar una solución más simple si es posible, que no depende de las versiones de Android (u otros componentes, que están fuera de su control)

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=211027&siteId=1
Recomendado
Clasificación