前段时间为了封装下cocos2dx游戏代码,就编译了2个平台下的开发库,window下的.lib和.dll,Android下的.a.和.so,以前也没有编译过,上网查资料1、要不就是copy代码上linux编译,2、要不就是用cywin模拟环境去编译。3、ndk编译(更多方法知之者告知)关键是linux都没有接触过,下手肯定不方便,所以就我而言用ndk最好(开发Android嘛,虽然只用了2个月,但好歹也是用过了的),下面就讲下如何编译.so及所遇到的问题。
一、工欲善其事,必先利其器
先要编译出.so文件,你得有工具啊,而这个工具就是ndk了,上网下载ndk安装,如果你是Android游戏开发者,应该已经有了,并配置环境变量NDK_ROOT,这个可是ndk-build命令要找的路径。还好网上有很多这方面的教程,不懂可以查查看下。
二、磨刀不误砍柴工
下载安装好ndk,你得保证安装正确啊,不然不是浪费生命嘛。很简单,在window命令行中,输入ndk-build。如果发现出现这样的情况,恭喜你,可以继续下一步了。
三、您睡在您的金子上
我们先查看下samples文件下的hello-gl2有哪些文件,然后看看编译完成生产哪些文件
从cmd中进入你安装的ndk目录中的samples,最后进入G:\adt\android-ndk-r9d\samples\hello-gl2,然后ndk-build(记住NDK_ROOT一定要配置,不然会出问题喔)
如果一切正常,上面就告诉了你生成了libg12jni.so而且这个.so啊,还是生成在libs/armeabi文件夹中的
好了生成.so就可以使用了,但是有个问题是,这个可不是我们自己想要的.so,那怎么办呢?很简单,就是把你需要编译的.cpp拷贝到这个里面再编译一遍(当然没这么简单,下面就一起来看看怎么操作吧)。
四、我睡在我的谋生工具上
1、先来看看的我的目录结构吧(我也是安装三,把代码copy到hello-jni中的喔)
2、为什么要android.mk呢?Android.mk就是一个makefile配置文件,帮你把C/C++的代码编译成动态库so的(真直接!)
关键Android,mk有了,就相当于支持模块化了,以后你要编译哪些cpp把放在一起,就可以了,是不是很方便!一起来看看我的Android.mk
<span style="font-size:14px;">LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CPPFLAGS := -fexceptions
LOCAL_MODULE := kernelengine
LOCAL_SRC_FILES := Data/MB_Cipher.cpp \
Data/MB_MD5.cpp \
Data/MB_Socket.cpp \
Data/MB_SocketThread.cpp \
Data/BriefSocket.cpp \
Data/MsgControl.cpp
#Data/shell.c \
#Data/sqlite3.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/cocos2d/iconv \
$(LOCAL_PATH)/cocos2d/iconv/include \
$(LOCAL_PATH)/cocos2d/iconv/libcharset \
$(LOCAL_PATH)/cocos2d/iconv/libcharset/lib \
$(LOCAL_PATH)/cocos2d/iconv/libcharset/include \
#$(LOCAL_PATH) \
#$(LOCAL_PATH)/jni\Data \
#$(LOCAL_PATH)/Data \
#$(LOCAL_PATH)/NetWork \
#$(LOCAL_PATH)/Define
LOCAL_WHOLE_STATIC_LIBRARIES += iconv
LOCAL_WHOLE_STATIC_LIBRARIES += libiconv_static
include $(BUILD_SHARED_LIBRARY)
$(call import-module,iconv)</span>
为了编译出.so我可是操碎了心了,查了整整2天时间,中途想过放弃,因为有同事编译出来了,但是用cocos2dx创建个新项目然后放到class中在proj.android编译出,这种方式,很多缺陷,链接了很多不必要的文件,所以整整大了3M。下面会来解读下几个我出错的地方,我也是才弄明白.mk字段的含义,挺好玩的。
1)因为编写网络,网络异常可以会时常出现,异常处理机制就很有必要的,这里在C++使用throw来抛出,而在ndk中想要识别就必须加上这个字段,不加是编译不过的,LOCAL_CPPFLAGS := -fexceptions
2)因为我的游戏平台是window下的开发的,使用的是unicode编码,而手机是使用UTF-8的编码方式,所以要转码,而进行转码的工具就是这个iconv库了。因此编译的时候要把他链接进来LOCAL_WHOLE_STATIC_LIBRARIES += iconv,但是你链接iconv,你还要导入模块啊,$(call import-module,iconv)。
使用命令$(call import-module,...)会出现一个问题,编译器会委婉的告诉你NDK_MODULE_PATH是不是定义的不合适啊?(你直接告诉是错的就好了呗!)
然后就是上网一整狂找了,说什么都有,都不知道哪个是对的,哎!后来发现这个跟cocos2dx版本有关系,我在想既然跟Cocos2dx版本有关系,而编译的时候都是在proj.android文件下build_native.py编译的,那我就看看这里面到底写了些什么。然后查找NDK_MODULE_PATH,果然被我发现了定义了。
看看说的多清楚,在win32平台下,ndk_module_path的值是cocos_root,cocos_root/external及cocos_root/cocos。于是,我把生成项目的Cocos2dx直接拷贝到hello-jni中(看四中的1目录结构就知道了),然后在环境变量中定义了NDK_MODULE_PATH(别说环境变量不会定义喔!)
3、为什么要Application.mk呢?Application.mk目的是描述在你的应用程序中所需要的模块(即静态库或动态库)
首先看看我写的Application.mk里都写了些什么?
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -DCC_ENABLE_CHIPMUNK_INTEGRATION=1 -DCOCOS2D_DEBUG=1 -std=c++11 -fsigned-char
这些字段的意义,如果不知道,可以查查,网上讲的非常详细。然后让我们来编译下。
啥?我XXXXX原本信誓旦旦信心满满的,结果这个问题recursive_mutex in namespace 'std' does not name a type 。。。。。thread is not a member of 'std',这坑爹的问题啊,为了这个问题,我搜了整整1天时间。意思我明白说std中没有recursive_mutex这个类型,thread不是std的成员,这不坑我吗?头文件我都加了#include<mutex>,#include <thread>,还是报错啊。。。。然后我就想,是不是我包含的库gnustl_static 里面没有这个头文件,于是我就去G:\adt\android-ndk-r9d\sources\cxx-stl\gnu-libstdc++\4.6\include下去找mutex和thread看看有没有,结果很遗憾的是:有!
所以我就排除的这个可能了,这一排除就坑惨我了,为此花出一整天时间(宁可相信世上有鬼,也不要相信自己主观瞎想,他说有mutex,thread可能是错的呢,可能是伪装的呢!)下面看看这个问题到底怎么解决的!
首先推荐2个网址:
1)http://jingyan.baidu.com/article/b87fe19ebd51fa52183568f7.html(ndk c++11 thread rtti exception stl这个对于thread和运行时异常处理整体流程说的挺好)
2)http://stackoverflow.com/questions/14191566/c-mutex-in-namespace-std-does-not-name-a-type(C++ mutex in namespace std does not name a type跟我出现的问题很类似)
这个网址上,有个人回答是这样的:Wow, looking around, apparently one of these (4.7 ones only) should now have support for it. I'm currently downloading one to test it and I'll let you know if it ends up working.
我拿百度翻译了下(哎!英语永远的伤。。):哇,环顾四周,显然其中一个(4.7个)现在应该有支持它。我正在下载一个测试它,我会让你知道,如果它结束了工作。
这时候看到这个我惊呆了,这个意思是4.7之前不支持?于是,我赶快去试试,G:\adt\android-ndk-r9d\sources\cxx-stl\gnu-libstdc++来到这个文件夹下,我发现只有4.6和4.8(我的ndk版本中)
很明显了,加载的g++中C++库是4.6版本的(因为4.7以前不支持thread,mutex)。关键试试看看,看看结果就知道猜想对不对了!
于是,在Application.mk中加上NDK_TOOLCHAIN_VERSION := 4.8,如果你ndk版本有4.7,你换上4.7应该也是可以的,具体我没试过,我用的是4.8是可以的。
NDK_TOOLCHAIN_VERSION := 4.8
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -DCC_ENABLE_CHIPMUNK_INTEGRATION=1 -DCOCOS2D_DEBUG=1 -std=c++11 -fsigned-char
3)include $(BUILD_SHARED_LIBRARY)生成的是.so文件,include $(BUILD_STATIC_LIBRARY)生产的是.a文件
1、下面我们先来编译.a文件(为什么要先编译.a文件,嘿嘿,因为.so文件会有问题)
结果是:成功!
2、再来编译下.so文件:
结果是:失败!
这个错误很好解决,网上有答案了,undefined reference to 'wcstombs'
在Application.mk中加上APP_PLATFORM := android-8就可以了。
3、这2个我也都编译出来了,让我纳闷的是,怎么静态链接库比动态链接库,还要小。。。。windows上编译出来的.dll可是比lib要小的啊。
4、至此全部编译出来了。