在 cocos2d-x 3.4 上遇到了一个诡异的问题,记录如下。

平台和版本

  • 框架: cocos2d-x 3.4 final
  • 设备: Nexus 4/5/7 with Android 5.0.1/5.0.2
  • NDK version: r9d

问题描述

一个 dragonbonesCPP 骨骼动画,在 iOS、Windows、Mac OS X、Android with 4.x 上表现完全正常,但在 Android 5.x 下表现不正常。

具体表现为解析骨骼动画数据的时候,既不 crash,也没有任何报错信息,整个程序就停住了。

问题追溯

通过加入 log 信息,发现问题处在 XMLDataParser::parserDragonBonesData 方法中,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//.............
DragonBonesData* XMLDataParser::parseDragonBonesData(const void *rawDragonBonesData, float scale) const
{
CCLOG("Start parse dragonbones data.");
_armatureScale = scale;
const XMLElement *dragonBonesXML = static_cast<const XMLElement*>(rawDragonBonesData);
std::string version = dragonBonesXML->Attribute(ConstValues::A_VERSION.c_str());
CCLOG("Start parse A_VERSION.");
// TODO
/*
switch(version)
{

}
*/
CCLOG("A_FRAME_RATE: %s .", ConstValues::A_FRAME_RATE.c_str());
//打印了上面一行log 之后,整个程序就停住了
_frameRate = dragonBonesXML->IntAttribute(ConstValues::A_FRAME_RATE.c_str());
CCLOG("Start parse A_FRAME_RATE %d.", _frameRate);
DragonBonesData *dragonBonesData = new DragonBonesData();
//................

IntAttribute 是 tinyxml2 提供的解析方法,继续追溯,发现程序在 tinyxml2 中的 XMLUtil::ToInt 方法中停住。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//.............
bool XMLUtil::ToInt(const char *str, int *value)
{
CCLOG("XMLUtil::ToInt str: %s, value: %d", str, *value);
//打印了上面一行 log 之后,整个程序就停住了
if (DBTIXML_SSCANF(str, "%d", value) == 1)
{
CCLOG("XMLUtil::ToInt SSCANF true");
return true;
}

CCLOG("XMLUtil::ToInt SSCANF false");
return false;
}
//.............

而 DBTIXML_SSCANF 是一个宏,在 NDK 中被指向 sscanf

我本以为是 tinyxml2 的问题,但换了 github 最新版本的 tinyxml2 依然有同样问题,于是开始怀疑 NDK。

Google 一番之后,找到了一个关键的 issue 77988 。这里说到的情况和我碰到的几乎一模一样,看来这是 NDK 的 BUG 没错了:

I have found that any call into the standard library sscanf function will cause the application to lockup. My device is the Nexus 5 running Android 5.0 preview version hammerhead-lpx13d-preview-f7596f51.tgz

Steps to recreate the problem:

  • Make any call to sscanf, for example:

    int v1 = 0;
    sscanf(“1”, “%d”, &v1);

  • Notice that the application will not return from the call to sscanf and completely freeze without any error message generated

解决方案

方案1

升级 NDK 到 r10d ,该版本已经解决了这个问题:

Documented a fix to a libc++ sscanf/vsscanf hang that occurred in API level 21. The fix itself had been implemented in r10c. (Issue 77988)

但进行 NDK 连接的时候,我碰到了这样的报错信息:

/Volumes/HD1/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: error: /Volumes/HD1/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/libgcc.a(pr-support.o): multiple definition of ‘_Unwind_GetRegionStart’
/Volumes/HD1/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: /Volumes/HD1/Android/android-ndk-r10d/sources/cxx-stl/llvm-libc++/libs/armeabi/thumb/libc++_static.a(Unwind-EHABI.o): previous definition here
/Volumes/HD1/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: error: /Volumes/HD1/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/libgcc.a(pr-support.o): multiple definition of ‘_Unwind_GetLanguageSpecificData’
/Volumes/HD1/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: /Volumes/HD1/Android/android-ndk-r10d/sources/cxx-stl/llvm-libc++/libs/armeabi/thumb/libc++_static.a(Unwind-EHABI.o): previous definition here
collect2: error: ld returned 1 exit status
make: *** [obj/local/armeabi/libcocos2dlua.so] Error 1

我没有去解决这个问题,因为还有下一个方案。

方案2

如果担心 cocos2d-x 3.4 和 NDK r10d 的兼容性,也可以修改 Android 的编译参数:

编辑 android 项目中的 Application.mk 文件:

vim frameworks/runtime-src/proj.android/jni/Application.mk

将第一行:

APP_STL := c++_static

改为:

APP_STL := gnustl_static

(全文完)

留言