原文地址:http://blog.csdn.net/sozell/article/details/10551309

cocos2dx 中 Android NDK 加载动态库的问题

闲聊

最近在接入各个平台的SDK,遇到了不少问题,也从中了解了不少知识,之前一直觉得没啥好写的,毕竟做了4个月的游戏开发,也没有碰上什么真正的大问题,cocos2dx的引擎包得也很好,能让人把大部分时间都关注在游戏逻辑、效果的处理上,当然,之前的libevent还是小坑一下,但是和后来遇到的相比,也算不上什么了。

我最早接入的SDK是360的,不知道是运气好还是点儿背,对于我这个只懂C++和lua以及一点点JAVA的人来讲,拿这个作为第一个练手,有点苦。当时对Activity这种完全没有概念的,不过也算挺过来了,回头看看也没啥太值得说道的。不过360的SDK里面就有libpaypalm_app_plugin_jar_360game.so这个动态库的接入,特别是和cocos2dx嵌入,还是有和原生的Android程序略有不同。不过还好这只有一个动态库,开始倒也用些不靠谱的方法搞定了,昨天开始接入中移动的支付SDK,里面有两个shared_library,用开始的方法搞不定了,不过今天晚上搜来搜去,终于发现点门道,希望这篇文章对和之前我一样各种苦逼的人一点帮助吧。不过深入原理我还是不懂的,别希望我能往深里挖,linux我还只局限在打几个shell命令的层次上。

需要了解的知识

  • 至少对cocos2dx有所了解(写这篇文章的时候,我用的是2.1.3版本),知道NDK是什么
  • 大致了解动态库和静态库的区别,知道*.so文件是什么
  • 会一点JAVA,有成功跑通过cocos2dx的程序(貌似不懂JAVA也可以跑通,呵呵)
  • 了解Android下的makefile的简单编写规则

我假定你是个cocos2dx的程序员,如果不是,估计帮助不大,因为我也不知道如何去类比这算是适合哪类人看的文章。

如果你正在搞三方动态库接入到cocos2dx中,那恭喜你,看过我这篇文章应该会有所帮助,我不会讲太深,反正底层的也不懂,还是以能用,知道如何用为主。

正题

一般支付SDK的接入,都有客户端和服务端,服务端不在本篇文章的讨论范围内,仅仅讲客户端,并且,这里只讲Android方面的,iOS的我也不怎么懂。反正所有的模式都是至少提供一个jar包,给Java作为调用入口,然后如果有涉及较为底层的操作,都会由一个.so动态库文件来搞定。一般来讲,如果是原生的Android程序,不用涉及到C/C++的操作,所以,这个so文件都是直接放到Android工程下的libs/armeabi文件夹下(当然针对CPU不同会有不同版本提供),至少他们的demo都会这样书写,并且的帮助文档中会告诉你,只要把这个so文件拷贝到对应的目录下即可。但是如果碰到cocos2dx,那就没那么好搞定了。因为本身这个框架的特性就决定了编写的都是动态库,然后由Activity去调用加载起来。Eclipse每次build都会把proj.android/libs/armeabi文件夹(其他CPU类型对应的文件夹也一样)清空,你把SDK中带的那些*.so文件拷贝过去没用,压根打包不进apk里面。这里就要用到一个makefile的动态库模块加载。

PREBUILT

其实本身cocos2dx生成的Android工程就是会加载很多prebuilt的,比如JPG处理啊,PNG处理啊,curl啊之类的,可以参考cocos2dx/platform/third_party/android/prebuilt目录,都放这里呢,不过不同的是这些都是静态库,我之前顺便加到项目中的iconv和libevent也都是照样画葫芦得加载进来的,但是现在碰到的是动态库,会有些许的不同,但是大致还是一样的套路。方法也就是在jni/Android.mk上做文章,在这个makefile中,把动态库模块罗列在这里,让程序去加载即可。这里需要去了解下NDK_MODULE_PATH是什么,还有makefile中的call import-module函数命令(不知道这样说是否正确)。

编写动态库(SHARED_LIBRARY)模块

说说就拿360说事儿。怎么把jar的SDK整合进去我就不展开了,具体说如何搞定里面的libpaypalm_app_plugin_jar_360game.so

首先需要编写一个独立的动态库模块,这里说的编写,其实就相当于声明一样,告诉编译器,去哪里找什么文件链接到程序罢了,只是写个makefile,Android.mk。你可以放在当前程序的jni下,新建一个libprebuilt文件夹,然后搞个armeabi的文件夹(其他文件夹类同,360貌似是提供了两个的,不过我编译的都是armeabi类型的),把libpaypalm_app_plugin_jar_360game.so这个动态库放进去。在libprebuilt目录下新建一个Android.mk,内容如下:

  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_MODULE    := paypalm_app_plugin_jar_360game
  4. LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libpaypalm_app_plugin_jar_360game.so
  5. include $(PREBUILT_SHARED_LIBRARY)

稍微解释下。

  • LOCAL_MODULE,这个是模块的名称,在其他地方引用这个模块,就填这个名字,一般是动态库去掉头部的lib和尾部的.so剩余的字符
  • LOCAL_SRC_FILES,如果是纯三方库的话,只需要填上库的路径即可,前面的TARGET_ARCH_ABI就是用来适配你编译的CPU类型的,会去对应的文件夹下找
  • PREBUILT_SHARED_LIBRARY,表明这个一个与编译的动态库,这点很重要,和静态库区分
如下 好了,剩下的就是在jni/Android.mk中去引用这个动态库了。
  1. LOCAL_SHARED_LIBRARIE := paypalm_app_plugin_jar_360game
 
做这一步还不够,因为makefile只知道要加载这个动态库,但是不知道去哪里加载,还需要把刚才编写的Android.mk包含进来,最简单的方法就是
  1. include $(LOCAL_PATH)/libprebuilt/Android.mk

好了,这样build的项目,会看到最后有Install libpaypalm_app_plugin_jar_360game.so的字样,在apk中的libs下也会找到对应的*.so文件,至少,我接入的360支付算是通了。如果还是不行,请再往下看,毕竟我没有去研究过原理,不敢打包票说一定能用。

 

如何加载两个.so

这个问题是我在接入移动MM的支付时遇到的,首先在makefile上折腾了好久。移动MM的支付有两个动态库需要加载,其实也可以用上面的方式来照样画葫芦,但是因为需要include两次(分别是两个*.so的模块),但是因为$(LOCAL_PATH)这个变量会变化(好多操作都会导致其变化,甚至在include一个makefile——有带$(LOCAL_PATH)的参数时也会导致直接定位到那个文件夹下,但是这样写出来的makefile会看起来很混乱)。其实完全可以使用之前加载静态库的模块的方式,这里可以用到call import-module函数来搞定。不过在说这个之前,有个概念要搞清楚,什么是NDK_MODULE_PATH,并且,这个东西是在哪里定义的。

什么是NDK_MODULE_PATH

这个我不多说,从字面上解释就是模块路径,其实也就是调用call import-module时的搜索路径。这个可以具体看篇简单的参考文章:点这里

NDK_MODULE_PATH是哪里定义的

这个其实搜下文件夹就知道了,它是定义在build_native.sh中的,我的版本中定义的是cocos2dx目录和对应的platform/third_party/android/prebuilt下。
 
好了,了解了以上两个小概念,就可以继续下去了。我们可以使用$(call import-module,xxx)来加载,把自己需要加载的模块(.so文件)和对应写好的Android.mk文件放到这个prebuilt文件夹下(自己分个类文件夹即可),然后直接call即可,参照其他静态库的加载方式,你自己的jni/Android.mk最下面都是的。然后再在makefile中加上你需要加载的动态库模块,如
  1. LOCAL_SHARED_LIBRARIES := identifyapp casdkjni

好了,build下试试,应该在最后会显示Install identifyapp.so和 Install casdkjni.so这样的输出,这表示这两个动态库被打包进了Apk中。但是不幸的是,经过这样,移动MM的支付调用起来还是会出现加载这几个so文件失败导致初始化失败的提示,其实就差最后一步。

 

关键一步

在主java文件中找到System.loadLibrary("cocos2dcpp");这句,然后在下面同样添上你要加载的三方库名称即可。貌似默认不指明的话,会到系统路径下去找so文件(没有root或system的权限,无权对这个文件夹操作),这几个so按道理是会装到data/appname/lib目录下的。
  1. static {
  2. System.loadLibrary("identifyapp");
  3. System.loadLibrary("casdkjni");
  4. System.loadLibrary("cocos2dcpp");
  5. }

加载顺序

这里有个问题要尤其注意,就是这些动态库的加载顺序,一定要放到libcocos2dcpp前加载,否则在载入libcocos2dcpp时,会因为没有之前这两个依赖的动态库而报错,报的就是对应的动态库木有载入

题外话

如果没有上面的主动load,但动态库是已经拷贝到对应的目录中去,我在搞的时候发现一个很神奇的现象。装上后,第一次启动就报错,报找不到identifyapp.so,反正怎么都起不来,然后再编译一个不带这两个动态库的版本,直接覆盖,竟然就好了,然后我把真机上的程序先卸载,再安装最后编译的版本,又报找不到动态库。反正一定要这个顺序安装两次不同的版本,就行了。毛估估其实覆盖安装的话是不会删除文件的,貌似不显示导入的话,程序自己会去当前app目录下自动加载动态库,而makefile中显示给出LOCAL_SHARED_LIBRARYS后,貌似就一定要load一下,否则找不到。没有去细究,但也算能给想细究的朋友一个小线索。
 

参考

  • 加载动态库部分可以参考stackflow上的这个帖子:请猛击我,反正这几天,这方面的文章也快被我翻烂了。
  • 关于Java加载JNI的C++动态库的资料可以参考这里: 请猛击我

【转载】cocos2dx 中 Android NDK 加载动态库的问题的更多相关文章

  1. Linux下c函数dlopen实现加载动态库so文件代码举例

    dlopen()是一个强大的库函数.该函数将打开一个新库,并把它装入内存.该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的.这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了. ...

  2. LoadLibrary加载动态库失败

    [1]LoadLibrary加载动态库失败的可能原因以及解决方案: (1)dll动态库文件路径不对.此场景细分为以下几种情况: 1.1 文件路径的确错误.比如:本来欲加载的是A文件夹下的动态库a.dl ...

  3. Windows平台LoadLibrary加载动态库搜索路径的问题

    一.背景 在给Adobe Premiere/After Effects等后期制作软件开发第三方插件的时候,我们总希望插件依赖的动态库能够脱离插件的位置,单独存储到另外一个地方.这样一方面可以与其他程序 ...

  4. QLibrary 加载动态库

    阅读本文大概需要 6.6分钟 一般情况下在没有头文件支持情况下,想要引入某个动态库,最好的办法就是使用「动态加载」的方法,在Qt中一般使用QLibyary来操作 常用 api QLibrary(con ...

  5. QT常用代码之加载动态库和弹出对话框

    作者:朱金灿 来源:http://blog.csdn.net/clever101 加载动态库的代码: typedef void (*Execute)(); // 定义导出函数类型 QString st ...

  6. 使用dlopen加载动态库

    目录 概述 接口 C CMakeLists.txt src/main.c src/add.c ./dlopen_test C++ CMakeLists.txt src/main.cpp src/add ...

  7. Android NDK加载SD卡中的so

    最近公司框架刚移植完成,由于框架程序要调用子程序,每个子程序都是一个so文件,有好几百个,把所有的so和apk打包不现实,及时可以升级维护也很麻烦.所以需要放SD卡中.考虑两种方式 1 放到设备中的 ...

  8. Ubuntu中程序部署时无法加载动态库的解决方法

    Ubuntu下修改环境变量的三种方法 添加环境变量无法解决,可尝试如下操作: sudo vim /etc/ld.so.conf 在ld.so.conf中加入动态库的目录... 然后 sudo ldco ...

  9. DLL中加载其它DLL使用LoadLibrary加载动态库失败的解决办法

    方式一 采用LoadLibraryEx 若DLL不在调用方的同一目录下,可以用LoadLibrary(L"DLL绝对路径")加载.但若调用的DLL内部又调用另外一个DLL,此时调用 ...

随机推荐

  1. 【转载】使用IntelliJ IDEA 配置Maven(入门)

    1. 下载Maven 官方地址:http://maven.apache.org/download.cgi 解压并新建一个本地仓库文件夹 2.配置本地仓库路径   3.配置maven环境变量      ...

  2. Oracle中的rownum 和rowid的用法和区别

    Oracle中的rownum 和rowid的用法和区别   1.rownum是伪列,是在获取查询结果集后再加上去的 (获取一条记录加一个rownum).对符合条件的结果添加一个从1开始的序列号. eg ...

  3. BZOJ 3572 [HNOI2014]世界树 (虚树+DP)

    题面:BZOJ传送门 洛谷传送门 题目大意:略 细节贼多的虚树$DP$ 先考虑只有一次询问的情况 一个节点$x$可能被它子树内的一个到x距离最小的特殊点管辖,还可能被管辖fa[x]的特殊点管辖 跑两次 ...

  4. linux下的vi的使用方法

    vi的使用: 一般指令模式: vi打开一个文件就直接进入一般指令模式,可以进行删除.复制.粘贴.但是不可以对文件的内容进行修改. 常用命令: ctrl + f 向下移动一页 ctrl + b 向上移动 ...

  5. PAT 1093. Count PAT's

    The string APPAPT contains two PAT's as substrings. The first one is formed by the 2nd, the 4th, and ...

  6. 《奋斗吧!菜鸟》 第八次作业:Alpha冲刺

    项目 内容 这个作业属于哪个课程 任课教师链接 作业要求 https://www.cnblogs.com/nwnu-daizh/p/11012922.html 团队名称 奋斗吧!菜鸟 作业学习目标 A ...

  7. Django——3 模板路径 模板变量 常用过滤器 静态文件的使用

    Django 模板路径 模板变量 过滤器 静态文件的加载 模板的路径,有两种方法来使用 设置一个总的templates在大项目外面,然后在sittings的TEMPLATES中声明 在每一个APP中创 ...

  8. 背包again

    Gy最近学习了01背包问题,无聊的他又想到了一个新的问题,给定n个物品的价值,和01背包一样,每个物品只能选1次或0次,求最小不能被得到的价值. 输入 第一行一个正整数T(T <= 100),表 ...

  9. 积木大赛 2013年NOIP全国联赛提高组

    题目描述 Description 春春幼儿园举办了一年一度的“积木大赛”.今年比赛的内容是搭建一座宽度为 n 的大厦,大厦可以看成由 n 块宽度为1的积木组成,第i块积木的最终高度需要是hi.在搭建开 ...

  10. java获取类名不包括路径

    class.getSimpleName(),就能获得仅仅的类名 class.getName()获得的是全路径的类名