Android 14 新特性代码 UUID.fromString & Matcher.matches 的细节改动(扒源码)

发布时间:2024年01月03日


前言

Android 14 已经出来好久好久了…

今天其他的暂且不论,单纯的讲一下 OpenJDK 17 更新的两点变更(扒源代码)~

  • 对正则表达式的更改
  • UUID 处理

首先,正则表达式的更改:现在,为了更严格地遵循 OpenJDK 的语义,不允许无效的组引用。您可能会看到 java.util.regex.Matcher 类抛出 IllegalArgumentException 的新情况,因此请务必测试应用中使用正则表达式的情形。如需在测试期间启用或停用此变更,请使用兼容性框架工具切换 DISALLOW_INVALID_GROUP_REFERENCE 标志。

其次,UUID 处理:现在,验证输入参数时,java.util.UUID.fromString() 方法会执行更严格的检查,因此您可能会在反序列化期间看到 IllegalArgumentException。如需在测试期间启用或停用此变更,请使用兼容性框架工具切换 ENABLE_STRICT_VALIDATION 标志。

巴拉巴拉,以上是官网的言论,诸君可以看官方的描述地址


UUID 处理的更改

原有代码逻辑~

 /**
     * Creates a {@code UUID} from the string standard representation as
     * described in the {@link #toString} method.
     *
     * @param  name
     *         A string that specifies a {@code UUID}
     *
     * @return  A {@code UUID} with the specified value
     *
     * @throws  IllegalArgumentException
     *          If name does not conform to the string representation as
     *          described in {@link #toString}
     *
     */
    public static UUID fromString(String name) {
        String[] components = name.split("-");
        if (components.length != 5)
            //仅会判断以-分割的数组长度是否等于5来抛出异常
            throw new IllegalArgumentException("Invalid UUID string: "+name);
        for (int i=0; i<5; i++)
            components[i] = "0x"+components[i];

        long mostSigBits = Long.decode(components[0]).longValue();
        mostSigBits <<= 16;
        mostSigBits |= Long.decode(components[1]).longValue();
        mostSigBits <<= 16;
        mostSigBits |= Long.decode(components[2]).longValue();

        long leastSigBits = Long.decode(components[3]).longValue();
        leastSigBits <<= 48;
        leastSigBits |= Long.decode(components[4]).longValue();

        return new UUID(mostSigBits, leastSigBits);
    }

新代码逻辑~

/**
     * Creates a {@code UUID} from the string standard representation as
     * described in the {@link #toString} method.
     *
     * @param  name
     *         A string that specifies a {@code UUID}
     *
     * @return  A {@code UUID} with the specified value
     *
     * @throws  IllegalArgumentException
     *          If name does not conform to the string representation as
     *          described in {@link #toString}
     *
     */
    public static UUID fromString(String name) {
        // BEGIN Android-changed: Java 8 behaviour is more lenient and the new implementation
        // might break apps (b/254278943).
        // Using old implementation for apps targeting Android older than U.
        // 取反! 如果设备的sdk版本,大于等于  UPSIDE_DOWN_CAKE(34)的话,且启用严格验证
        if (!(VMRuntime.getSdkVersion() >= VersionCodes.UPSIDE_DOWN_CAKE
                && Compatibility.isChangeEnabled(ENABLE_STRICT_VALIDATION))) {
            return fromStringJava8(name);
        }
        //如果小于或者没开启严格验证,则执行其他的验证 
        return fromStringCurrentJava(name);
        // END Android-changed: Java 8 behaviour is more lenient and the new implementation
        // might break apps (b/254278943).
    }

如下是java8 的判断方式(跟原来一致)

  /**
     * Extracted for testing purposes only.
     * @hide
     */
    public static UUID fromStringJava8(String name) {
        String[] components = name.split("-");
        if (components.length != 5)
            throw new IllegalArgumentException("Invalid UUID string: "+ name);
        for (int i=0; i<5; i++)
            components[i] = "0x"+components[i];

        long mostSigBits = Long.decode(components[0]).longValue();
        mostSigBits <<= 16;
        mostSigBits |= Long.decode(components[1]).longValue();
        mostSigBits <<= 16;
        mostSigBits |= Long.decode(components[2]).longValue();

        long leastSigBits = Long.decode(components[3]).longValue();
        leastSigBits <<= 48;
        leastSigBits |= Long.decode(components[4]).longValue();

        return new UUID(mostSigBits, leastSigBits);
    }

如下是执行34或者开启严格验证的方法:

/**
     * Extracted for testing purposes only.
     * @hide
     */
    public static UUID fromStringCurrentJava(String name) {
        if (name.length() == 36) {
            char ch1 = name.charAt(8);
            char ch2 = name.charAt(13);
            char ch3 = name.charAt(18);
            char ch4 = name.charAt(23);
            if (ch1 == '-' && ch2 == '-' && ch3 == '-' && ch4 == '-') {
                long msb1 = parse4Nibbles(name, 0);
                long msb2 = parse4Nibbles(name, 4);
                long msb3 = parse4Nibbles(name, 9);
                long msb4 = parse4Nibbles(name, 14);
                long lsb1 = parse4Nibbles(name, 19);
                long lsb2 = parse4Nibbles(name, 24);
                long lsb3 = parse4Nibbles(name, 28);
                long lsb4 = parse4Nibbles(name, 32);
                if ((msb1 | msb2 | msb3 | msb4 | lsb1 | lsb2 | lsb3 | lsb4) >= 0) {
                    return new UUID(
                            msb1 << 48 | msb2 << 32 | msb3 << 16 | msb4,
                            lsb1 << 48 | lsb2 << 32 | lsb3 << 16 | lsb4);
                }
            }
        }
        return fromString1(name);
    }

    private static UUID fromString1(String name) {
        int len = name.length();
        if (len > 36) {
            throw new IllegalArgumentException("UUID string too large");
        }

        int dash1 = name.indexOf('-', 0);
        int dash2 = name.indexOf('-', dash1 + 1);
        int dash3 = name.indexOf('-', dash2 + 1);
        int dash4 = name.indexOf('-', dash3 + 1);
        int dash5 = name.indexOf('-', dash4 + 1);

        // For any valid input, dash1 through dash4 will be positive and dash5
        // negative, but it's enough to check dash4 and dash5:
        // - if dash1 is -1, dash4 will be -1
        // - if dash1 is positive but dash2 is -1, dash4 will be -1
        // - if dash1 and dash2 is positive, dash3 will be -1, dash4 will be
        //   positive, but so will dash5
        if (dash4 < 0 || dash5 >= 0) {
            throw new IllegalArgumentException("Invalid UUID string: " + name);
        }

        long mostSigBits = Long.parseLong(name, 0, dash1, 16) & 0xffffffffL;
        mostSigBits <<= 16;
        mostSigBits |= Long.parseLong(name, dash1 + 1, dash2, 16) & 0xffffL;
        mostSigBits <<= 16;
        mostSigBits |= Long.parseLong(name, dash2 + 1, dash3, 16) & 0xffffL;
        long leastSigBits = Long.parseLong(name, dash3 + 1, dash4, 16) & 0xffffL;
        leastSigBits <<= 48;
        leastSigBits |= Long.parseLong(name, dash4 + 1, len, 16) & 0xffffffffffffL;

        return new UUID(mostSigBits, leastSigBits);
    }

解析:
从代码的角度看起来是多了好多验证,梳理一下:

1. 判断是否34(sdk版本),且未开启严格验证的情况下,还是按照原有代码执行,仅仅判断 以-分割的数组长度是否等于5,若不等于5,则主动抛出异常;

2. 如果sdk版本大于等于34,且开启了严格验证,则会执行fromStringCurrentJava()& fromString1()方法,

fromStringCurrentJava方法中没有主动抛出异常的代码,判断长度是否=36,不等于则执行 fromString1,若等于则继续执行,获取字符串下标8、13、18、23的值,若这四个值不等于 - ,则执行 fromString1;
在fromString1; 方法中,传递的字符串长度不得大于36,若超出则主动抛异常;

继续判断;对于任何有效输入,dash1 到 dash4 将为正数,dash5 将为正数,负数,但检查 dash4 和 dash5 就足够了:
若是dash4小于0,dash5大于等于0,则主动抛出异常;

正则表达式的更改

原有代码逻辑~

/**
     * Attempts to match the entire region against the pattern.
     *
     * <p> If the match succeeds then more information can be obtained via the
     * <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods.  </p>
     *
     * @return  <tt>true</tt> if, and only if, the entire region sequence
     *          matches this matcher's pattern
     */
    public boolean matches() {
        synchronized (this) {
            matchFound = nativeMatcher.matches(groups);
        }
        return matchFound;

新代码逻辑~

 /**
     * Attempts to match the entire region against the pattern.
     *
     * <p> If the match succeeds then more information can be obtained via the
     * {@code start}, {@code end}, and {@code group} methods.  </p>
     *
     * @return  {@code true} if, and only if, the entire region sequence
     *          matches this matcher's pattern
     */
    public boolean matches() {
        synchronized (this) {
            matchFound = nativeMatcher.matches(groups);
        }
        //主要增加一个这个
        modCount++;
        return matchFound;
    }

modCount的描述 :记录该匹配器状态被修改的次数


    /**
     * Number of times this matcher's state has been modified
     */
    int modCount;

结束

不是我说哈,你都是系统代码了,还起个 fromString1 这样的方法名称,丢人不~
就这,有问题私聊我~
再会~

文章来源:https://blog.csdn.net/a_Chaon/article/details/135370553
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。