作者:庄晓立 (liigo)
日期:2011.6.15
原创链接:http://blog.csdn.net/liigo/archive/2011/06/14/6544649.aspx
转载请注明出处:http://blog.csdn.net/liigo
我(liigo)以前的围棋SGF棋谱解析器是使用C语言开发的,为了省事和加快移植,就不打算用Java语言重写了,计划通过 JNI (Java Native Interface) 在JAVA中调用C语言的代码,自然的,Android NDK 就派上用场了。我此前只是对NDK稍有耳闻,从来没用过。上周末抽了大概一天的时间实战试用 Android NDK 初见成效,收获不小。略加记录,以备后用。
一开始当然是先下载NDK(发现android.com已在墙内了),阅读其官方使用文档。文档中提到NDK在Windows下需要依赖Cygwin(却没有指导性操作提示),去cygwin.com下载一setup.exe为在线下载安装程序。由于NDK没有相关说明,我在安装cygwin问题上费了一些周折,经过网络搜索,在安装程序配置界面指定安装devel类别,设定为Install(见下图),以便确保下载安装得到其中的gcc和make程序。下载过程可能比较漫长,耐心等待吧。Devel里可能有许多不是NDK所必需的内容(导致cygwin安装目录大幅膨胀),多了就多了吧,总比缺胳膊少腿好。安装完成后,尝试在cygwin控制台运行gcc和make命令,有恰当输出反馈说明安装成功了(可以满足NDK需要),如果有SB的“command not found”之类错误提示(这里借用一网友语气),说明你还得考虑重新下载安装cygwin。
NDK和Cygwin都有了,接下来怎么办,我也糊涂了,NDK文档似乎没有说怎么把两者结合起来使用。因为发现NDK中的ndk-build是shell脚本,需要linux环境才能运行,初步想到去cygwin控制台命令行执行 ndk-build 命令,结果进去一看,全是 /home, /usr, /dev 之类Linux目录,我的NDK在windows系统中的D盘呀,怎么访问到?又是借助于网络搜索,知道了在cygwin内 /cygdrive 就可以访问当前Windows系统下的磁盘各分区,如 /cygdrive/c 表示C盘,/cygdrive/d 表示D盘。OK,按照NDK文档,执行命令:
$PROJECT、$NDK 都是环境变量,以后再定义吧,先用绝对完整路径吧,写成这样:
每次都写这么长的路径谁受得了,得了,定义成环境变量吧,再次搜索网络,修改 <cygwin>/home/liigo/.bash_profile 这个文件(其中liigo目录应该是cygwin安装时根据当前windows用户生成的),在最后加入以下几行:
以后编译命令行就简化了:
这中间还出现一个插曲,我是用Windows系统自带的“写字板”程序(wordpad.exe)编辑修改 .bash_profile 文件的,结果进入cygwin控制台时它提示无法解析配置文件。分析发现写字板保存的文本文件行尾是"/r/n",而cygwin要求配置文件行尾是"/n",我一时没有趁手达到此要求的文本编辑器,于是我写了几行易语言代码(这里下载源码)解决了这个问题:
还是各有各的办法,你要是有EditPlus或者EmEditor或者UltraEdit,一个另存为就能搞定的事。我现在在想,当时为什么没想到用字节集替换的方法,还可以少写好几行代码,失误失误。
接下来算是真正进入正题。不过对我(liigo)来说好像有点轻车熟路。前些年使用JNI的经历,现在还有些印象,所以不觉得困难。先编写含有native方法的Java Class,用java编译器编译之;然后用javah处理刚才编译生成的class,生成用于实现对应native方法的C/C++头文件,包含相应的函数原型声明;自己创建对应的.c/.cpp文件,#include刚才生成的头文件,定义并实现其中的函数(代码见下文)。我是在记事本中完成这些函数编码的,这不是装B,实在是暂时没想到有其他更好的办法,Eclipse IDE似乎也没提供什么支持,且不说NDK其实跟ADT甚至没什么直接关联。好在这个过程是比较顺利的,中间自然少不了随时查看JNI官方文档中的接口函数说明。
代码写完之后,参照NDK文档,在Eclipse项目目录jni子目录内创建一个Android.mk文本文件,其内容就从NDK文档中抄下来稍作修改(修改了第三行和第四行):
然后进cygwin,执行编译命令:
如果遇到错误则根据提示做相应修改,重新编译。成功之后,NDK会在eclipse项目目录内生成一些文件(例如libs目录内的动态库.so)。此时回到Eclipse IDE,重新编译Android项目,生成的.apk就是包含有调用C/C++代码的应用软件了。我在模拟器中部署此apk时貌似还出现了一些部署性错误,导致apk无法正常启动,后来又莫名其妙好了,似乎需要重启eclipse IDE并重新编译整个项目?存疑。
现在总结一下NDK的作用,它提供一些库文件和头文件,提供交叉编译的手段(调用gcc,make),编译生成必要的文件到项目目录(供ADT打包.apk)。至于在Java中调用C函数(通过native方法),和在C/C++中调用Java方法或读写对象成员(fields),主要是Java本身的JNI的功劳。
在ndk-build编译过程中,我主要遇到了以下两种编译错误,C/C++语法错误,导致了一大批编译错误。第一个是调用JNI函数时,我参照其官方文档,想当然的编写代码为类似如下:
其实在C++(.cpp文件)中正确的写法应该是:
C语言(.c文件)中正确的写法应该是:
遇到的另一个错误是结构体(struct)的预声明语法,我原来的写法在VC编译器中是可以编译的:
但是在gcc(g++)编译器中不认可这种写法,说是类型重定义,修改为以下代码后编译通过:
后来经过测试,各种功能正常调用成功,说明NDK正确的发挥了它的作用。下面贴一些代码,仅供参考。
首先是 SgfParser.java:
然后是com_liigo_go_SgfParser.cpp: