String.format与手机设置的语言环境的关系导致语言转换系统无法识别闪退问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011835956/article/details/73163044

问题原因:线上闪退日志突然出现同一个用户几千条闪退日志,且都是同一个闪退 no such column: ٥٠٠ (code 1): , while compiling: SELECT type, wid FROM MoPeibo LIMIT ٠,٥٠٠。调查发现٠,٥٠٠是阿拉伯语中的0,500。我们代码是使用String.format来将0和500这两个int类型的值转为string”0,500”。转换代码如下:

String limit = String.format("%d,%d", new Object[]{Integer.valueOf(offset), Integer.valueOf(500)});

也就是说,String.format会考虑本地的语言环境(该问题中,由于手机在设置里面切换语言环境为阿拉伯语,导致上面代码在format的时候将“0,500”转为阿拉伯语“٠,٥٠٠ ”数据库无法识别,结果导致异常):
format(Locale l, String format, Object… args) 使用指定的语言环境、格式字符串和参数返回一个格式化字符串。
format(String format, Object… args) 使用指定的格式字符串和参数返回一个格式化字符串。
后面又测试,发现如下写法不会导致上述问题:

String limit = String.format("%s,%s", String.valueOf(offset), String.valueOf(500));

开始觉得特别奇怪,调用相同的方法为什么有的会根据语言环境转换而有的则不会,查看了源码,发现源码中会对d,s分别处理,除了d和时间类型转换其他的类型都不会进行语言转换。猜想原因是各个国家的数字表示都不同,做了特殊处理。其实到底为啥数字和时间类型要根据语言切换我也不明白,反正源码就是这样写的。以下是源码:

if (token.isDefault()) {
    switch (token.getConversionType()) {
    case 's':
        if (arg == null) {
            return "null";
        } else if (!(arg instanceof Formattable)) {
            return arg.toString();
        }
        break;
    case 'd':
        boolean needLocalizedDigits = (localeData.zeroDigit != '0');
        if (out instanceof StringBuilder && !needLocalizedDigits) {
            if (arg instanceof Integer || arg instanceof Short || arg instanceof Byte) {
                IntegralToString.appendInt((StringBuilder) out, ((Number) arg).intValue());
                return null;
            } else if (arg instanceof Long) {
                IntegralToString.appendLong((StringBuilder) out, ((Long) arg).longValue());
                return null;
            }
        }
        if (arg instanceof Integer || arg instanceof Long || arg instanceof Short || arg instanceof Byte) {
            String result = arg.toString();
            return needLocalizedDigits ? localizeDigits(result) : result;
        }
    }
}

以上只拿出了部分源码,大概都是这个意思,是通过localizeDigits这个方法根据ASCII将数字0-9转为对应语言的字符,如下:

private CharSequence localizeDigits(CharSequence s) {
    int length = s.length();
    int offsetToLocalizedDigits = localeData.zeroDigit - '0';
    StringBuilder result = new StringBuilder(length);
    for (int i = 0; i < length; ++i) {
        char ch = s.charAt(i);
        if (ch >= '0' && ch <= '9') {
            ch += offsetToLocalizedDigits;
        }
        result.append(ch);
    }
    return result;
}

总结:在使用String.format以及new SimpleDateFormat(“yyyy-MM-dd”)时候一定要注意处理语言问题,不然很可能引起系统无法识别的错误。处理方式一个是全局指定语音环境,一个是在调用方法时指定语言环境。
全局(自己的Application):

String languageToLoad  = "zh";
Locale locale = new Locale(languageToLoad);
Locale.setDefault(locale);
Configuration config = getResources().getConfiguration();
DisplayMetrics metrics = getResources().getDisplayMetrics();
config.locale= Locale.SIMPLIFIED_CHINESE;
getResources().updateConfiguration(config,metrics);

调用方法:

format(Locale l, String format, Object… args) 使用指定的语言环境、格式字符串和参数返回一个格式化字符串。

猜你喜欢

转载自blog.csdn.net/u011835956/article/details/73163044