android音乐播放器歌词中文乱码,android原生态音乐播放器中文歌曲乱码问题——没落的MIPS...

本文基于Android 4.2,分析了MIPS架构上中文歌曲名显示为“?”的问题。通过跟代码发现是utf8_length函数返回 -1 导致,进一步分析得知是char类型扩展时,MIPS有符号扩展使utf32值过大。解决办法是将first_char声明为无符号类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于android4.2,主要出现在mips架构,arm架构上没有此现象。

猜测与MediaScanner有关,一直跟代码,跟啊跟,跟啊跟。跟到frameworks/base/media/jni/android_media_MediaScanner.cpp文件的handleStringTag函数,它的实现如下:

119     virtual status_t handleStringTag(const char* name, const char* value)

120     {

121         ALOGE("handleStringTag: name(%s) and value(%s)", name, value);

122         jstring nameStr, valueStr;

123         if ((nameStr = mEnv->NewStringUTF(name)) == NULL) {

124             mEnv->ExceptionClear();

125             return NO_MEMORY;

126         }

127

128         // Check if the value is valid UTF-8 string and replace

129         // any un-printable characters with '?' when it's not.

130         char *cleaned = NULL;

131         if (utf8_length(value) == -1) {

132             cleaned = strdup(value);

133             char *chp = cleaned;

134             char ch;

135             while ((ch = *chp)) {

136                 if (ch & 0x80) {

137                     *chp = '?';

138                 }

139                 ALOGE("handleStringTag: value(%x)",ch);

140                 chp++;

141             }

142             value = cleaned;

143         }

144         valueStr = mEnv->NewStringUTF(value);

145         free(cleaned);

146         if (valueStr == NULL) {

147             mEnv->DeleteLocalRef(nameStr);

148             mEnv->ExceptionClear();

149             return NO_MEMORY;

150         }

151

152         mEnv->CallVoidMethod(

153             mClient, mHandleStringTagMethodID, nameStr, valueStr);

154

155         mEnv->DeleteLocalRef(nameStr);

156         mEnv->DeleteLocalRef(valueStr);

157         return checkAndClearExceptionFromCallback(mEnv, "handleStringTag");

158     }看131行,就是utf8_length()这个函数调用返回-1,才导致歌曲的名字都设置成了“?”。其实已经测试过了,arm架构返回的值不等于-1.

那就看下函数utf8_length()的实现,找到在frameworks/native/libs/utils/Unicode.cpp中:

364 ssize_t utf8_length(const char *src)

365 {

366 const char *cur = src;

367 size_t ret = 0;

368 while (*cur != '\0') {

369 const char first_char = *cur++;

370 if ((first_char & 0x80) == 0) { // ASCII

371 ret += 1;

372 continue;

373 }

374 // (UTF-8's character must not be like 10xxxxxx,

375 // but 110xxxxx, 1110xxxx, ... or 1111110x)

376 if ((first_char & 0x40) == 0) {

377 return -1;

378 }

379

380 int32_t mask, to_ignore_mask;

381 size_t num_to_read = 0;

382 char32_t utf32 = 0;

383 for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;

384 num_to_read < 5 && (first_char & mask);

385 num_to_read++, to_ignore_mask |= mask, mask >>= 1) {

386 if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx

387 return -1;

388 }

389 // 0x3F == 00111111

390 utf32 = (utf32 << 6) + (*cur++ & 0x3F);

391 }

392 // "first_char" must be (110xxxxx - 11110xxx)

393 if (num_to_read == 5) {

394 return -1;

395 }

396 to_ignore_mask |= mask;

397 utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));

398 if (utf32 > kUnicodeMaxCodepoint) {

399 return -1;

400 }

401

402 ret += num_to_read;

403 }

404 return ret;

405 }看起来像是判断utf-8的格式,返回-1的地方有多个,测试了一下是在399行返回的,utf32的值大于kUnicodeMaxCodepoint,kUnicodeMaxCodepoint的定义如下:

static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF;打印了一下(~to_ingnore_mask)、first_char和num_to_read的16进制的值,发现first_char的高位全是F,这个非常不正常。first_char的定义在369行:

const char first_char = *cur++;

一个正常的char类型的高位不是F的,为何有这样的结果?慢慢分析后发现first_char与mask以及to_ingnore_mask这两个变量运算过,它们的类型是int32_t,类型不一样就涉及到类型转换。原来,在char类型扩展到32位时,高位要补0。在arm架构里,char类型的默认扩展是无符号扩展,而在mips是有符号扩展,所以高位全补了1,导致运算出来的utf32的高位全是F,所以它的值大于kUnicodeMaxCodepoint。

如何解决?其实很简单,把first_char声明为无符号类型就可以了:

const unsigned char *first_char = *cur++;

其实这个是google程序员写程序时没注意到的细微的地方,但是在goldfish上跑明显有中文歌曲乱码的bug存在,在这种情况下也把源码发布,这个就有点说不过去。结合之前TraceView Issue 这篇文章,可以看出android在支持mips架构时已经不想花太多的精力。不过市场上的android设备跑mips的确实不多。君正不知道还有没有搞mips的soc!mips注定要没落吗!?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值