android原生态音乐播放器中文歌曲乱码问题——没落的MIPS
基于android4.2,主要出现在mips架构,arm架构上没有此现象。
猜测与MediaScanner有关,一直跟代码,跟啊跟,跟啊跟。跟到frameworks/base/media/jni/android_media_MediaScanner.cpp文件的handleStringTag函数,它的实现如下:
119virtual status_t handleStringTag(const char* name, const char* value)
120{
121ALOGE("handleStringTag: name(%s) and value(%s)", name, value);
122jstring nameStr, valueStr;
123if ((nameStr = mEnv->NewStringUTF(name)) == NULL) {
124mEnv->ExceptionClear();
125return 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.
130char *cleaned = NULL;
131if (utf8_length(value) == -1) {
132cleaned = strdup(value);
133char *chp = cleaned;
134char ch;
135while ((ch = *chp)) {
136if (ch & 0x80) {
137*chp = '?';
138}
139ALOGE("handleStringTag: value(%x)",ch);
140chp++;
141}
142value = https://www.it610.com/article/cleaned;
143}
144valueStr = mEnv->NewStringUTF(value);
145free(cleaned);
146if (valueStr == NULL) {
147mEnv->DeleteLocalRef(nameStr);
148mEnv->ExceptionClear();
149return NO_MEMORY;
150}
151
152mEnv->CallVoidMethod(
153mClient, mHandleStringTagMethodID, nameStr, valueStr);
154
155mEnv->DeleteLocalRef(nameStr);
156mEnv->DeleteLocalRef(valueStr);
157return 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 {
366const char *cur = src;
367size_t ret = 0;
368while (*cur != '\0') {
369const char first_char = *cur++;
370if ((first_char & 0x80) == 0) { // ASCII
371ret += 1;
372continue;
373}
374// (UTF-8's character must not be like 10xxxxxx,
375//but 110xxxxx, 1110xxxx, ... or 1111110x)
376if ((first_char & 0x40) == 0) {
377return -1;
378}
379
380int32_t mask, to_ignore_mask;
381size_t num_to_read = 0;
382char32_t utf32 = 0;
383for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
384num_to_read < 5 && (first_char & mask);
385num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
386if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx
387return -1;
388}
389// 0x3F == 00111111
390utf32 = (utf32 << 6) + (*cur++ & 0x3F);
391}
392// "first_char" must be (110xxxxx - 11110xxx)
393if (num_to_read == 5) {
394return -1;
395}
396to_ignore_mask |= mask;
397utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
398if (utf32 > kUnicodeMaxCodepoint) {
399return -1;
400}
401
402ret += num_to_read;
403}
404return 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行:
【android原生态音乐播放器中文歌曲乱码问题——没落的MIPS】
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注定要没落吗!?
推荐阅读
- android第三方框架(五)ButterKnife
- Android中的AES加密-下
- 带有Hilt的Android上的依赖注入
- android|android studio中ndk的使用
- 病态与非病态的梦中人情结|病态与非病态的梦中人情结 - 草稿
- 夏天了,来一首入耳即化的音乐吧
- Android事件传递源码分析
- RxJava|RxJava 在Android项目中的使用(一)
- Android7.0|Android7.0 第三方应用无法访问私有库
- 深入理解|深入理解 Android 9.0 Crash 机制(二)