JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal continuation byte 0x2d

bug 描述 :cordova项目 ,名字字段中使用了输入法中自带的表情文件,在网站上一切正常,同步到sqlite数据库的时候会显示乱码。同时一旦对该条数据进行查询操作,就会导致程序奔溃。

Modified UTF-8

      所谓的MUTF-8编码,其实是对UTF-16字符编码的再编码。

      如果你的Android App里用了C++代码去处理一些字符, 可能会遇到崩溃input is not valid Modified UTF-8. 这个崩溃的原因在于编码不一致. 外部往往使用的是标准UTF-8编码, JNI里相关方法处理的是变种UTF-8编码, 虽说他俩名字上都有UTF-8, 但是本质上还是两种编码方式, 尽管他们有一部分是兼容的.
        JNI里, 凡是和String相关的方法, 名字带有UTF的, 基本都是使用的MUTF-8(变种UTF-8, Modified UTF-8).
比如
jstring NewStringUTF(JNIEnv *env, const char *bytes);是使用MUTF-8的字节流构造jstring.
const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);获取jstring以MUTF-8编码的字节数组.

但是Java里, 凡是和UTF-8相关的字符串操作, 都是使用标准UTF-8.
String.getBytes("UTF-8")拿到的字符串的标准UTF-8编码的字节数组
new String(bytes, "UTF-8")是使用标准UTF-8的字节流构造String.

许多研发并不知道UTF-8在Java层和JNI层的差异, 所以一旦遇到这种不同编码方式导致的问题, 会很懵逼.

在Android的官方Dex文件格式的文档中,对MUTF-8编码有如下描述,总结的很到位:

1)MUTF-8使用1到3个字节对UTF-16字符进行编码;

2)对于数值为0的情况,使用两个字节对其进行编码(编码后的值为0xC0和0x80);

3)采用类似于C语言中的空字符串(NULL,单字节数值为0)作为字符串结尾的标志;

4)对于UTF-16码点范围在U+10000到U+10FFFF的情况(补充字符),数值对中的每一个数值采用3字节对其编码。也就是说,对于这种情况,表示一个字符总共需要使用6个字节。

前面三点很好理解,对于第四点,理解起来有点困难,这里特别说明一下。

大家知道UTF-16使用16位来对字符进行编码,那么其取值范围就应该是0x0到0xFFFF,这已经可以表示很多字符了。但是,世界太大了,要表示的字符太多了,最终发现16位不够用了。那怎么办呢,只能继续扩展,将取值范围又向上扩展,从0x10000到0x10FFFF,称作扩展字符。这些扩展字符的值,显然不能再用16位来表示了,那就用两个16位值来表示。对于这种表示一个扩展字符使用两个16位数值的情况,UTF-16称作代替数值对(Surrogate Pair),其编码规则如下:

1)先将UTF-16补充码的数值减去0x10000;

2)将减掉之后的数值分为两个10比特的数值,假设高10位的值表示为Vh,低10位的值表示为Vl;

3)对于数值对中第一个16位的双字节来说,用0xD800加上高10位的值Vh;

4)对于数值对中第二个16位的双字节来说,用0xDC00加上低10位的值Vl。

MUTF-8和标准UTF-8的区别有两点.
        一是空字符("\0")在MUTF-8里被编码为两个字节: 0xC080(即1100000010000000). 它在标准UTF-8里编码为0x00.
        二是SP内的字符, 首先以UTF-16的编码方式编为一个前导代理和一个后尾代理, 然后再用标准UTF-8分别编码这两个代理.
BMP内的字符, 除了\0, 其他的MUTF-8和UTF-8编码方式相同.
由于SP范围是U+10000 - U+10FFFF, 标准UTF-8将SP内的码位编码为4字节, 而代理区U+D800 - U+DFFF中的码位在标准UTF-8中被编为3字节. 因此MUTF-8对SP内的码位进行编码需要3+3 = 6字节, 比标准UTF-8多2字节. dex里的字符串以MUTF-8编码.

在调用NewStringUTF()时, dalvik虚拟机调用checkUtfString() 中的checkUtfBytes()对字符串的格式进行了校验.

the key point is "Modified UTF-8" is not like "Regular UTF-8", a legal Rgular UTF8 code sequence may be considered illegal against Modified UTF8.

One work around in NDK level is : converting utf8 to native utf16, then use NewString() instead of NewStringUTF().

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值