标题: android平台中编写jni模块的方法(2) [打印本页] 作者: chenwayne 时间: 2009-07-09 12:05 标题: android平台中编写jni模块的方法(2) 北京理工大学 20981 陈罡
继续上一篇,目前android平台的sdk已经发布到了cupcake 1.5的版本(最新的开发版可能要比这个版本更高,期待android 2.0 :D)
对于android 1.5版本的开发者而言,一个非常大的好消息是cupcake已经开始“官方”支持开发者编写自己的jni库了,这主要表现在google放出了一个叫做android-ndk的开发包,这个开发包是专门为了开发jni而准备的一些必要的头文件以及一些运行时所需的库,为android应用开发者提供了比较方便的脚本支持。这种方便和快捷是在android 1.0和1.1的sdk以及配套的工具里面是没有的。试用了ndk以后感觉良好,偶对于之前没有使用ndk,纯粹采用第三方编译工具进行jni开发尝试的“土法”进行一次归纳和整理,并且发布一个偶做测试用的jni代码。
从ndk的声明里面偶可以看到,google在使用jni技术进行开发的时候提出的几点需要主意的地方:
The NDK is *not* a good way to write generic native code that runs on Android
devices. In particular, your applications should still be written in the Java
programming language, handle Android system events appropriately to avoid the
"Application Not Responding" dialog or deal with the Android application
life-cycle.
Note however that is is possible to write a sophisticated application in
native code with a small "application wrapper" used to start/stop it
appropriately.
A good understanding of JNI is highly recommended, since many operations
in this environment require specific actions from the developers, that are
not necessarily common in typical native code. These include:
- not being able to directly access the content of VM objects through
direct native pointers. E.g. you cannot safely get a pointer to a
String object's 16-bit char array to iterate over it in a loop.
- requiring explicit reference management when the native code wants to
keep handles to VM objects between JNI calls.
The NDK only provides system headers for a very limited set of native
APIs and libraries supported by the Android platform. While a typical
Android system image includes many native shared libraries, these should
be considered an implementation detail that might change drastically between
updates and releases of the platform.
If an Android system library is not explicitely supported by the NDK
headers, then applications should not depend on it being available, or
they risk breaking after the next over-the-air system update on various
devices.
Selected system libraries will gradually be added to the set of stable NDK
APIs.
从上述观点可以看出,google对于dalvik本身的稳定性以及android的framework的信心还是很足的,所以不建议开发者使用jni技术(偶个人认为,他们是在担心开发者的水平高低不同,会影响到他们系统的稳定性),同时也告诫开发者即使使用了jni技术也未必会大幅度提升程序的运行效率(对于这一点偶表示怀疑,java终究是java,在vm上面晃荡的脚本的运行效率怎么可能跟优化过的native程序相比呢?当然对于这个问题是仁者见仁,智者见智了。)
总之,虽然google提供了ndk可以较为方便地构建android平台的jni模块,但是作为对于凡事喜欢刨根问底的偶来说,光知道运行几个脚本跟不懂没有任何区别,偶希望的是从原理上去了解android的jni技术。
于是,开始了这次“土法”建jni的过程:
(1)工具链的准备工作
第一步,当然是jdk以及android的sdk这些就不再赘述了。
第二不,登陆http://www.codesourcery.com的download页面,下载arm-2008q3-72-arm-none-linux-gnueabi.bin这个编译工具(偶是linux环境,不同的os可能稍微有些区别)
然后就是安装,设置环境变量,偶把自己的环境变量设定贴出来备份一下:
# for midnight commander default editor, i hate vi or vim (sorry for vi-ers)
export EDITOR=emacs
export VIEWER=emacs
# for splint
export LARCH_PATH=/usr/local/share/splint/lib
export LCLIMPORTDIR=/usr/local/share/splint/imports
export PATH=/usr/local/bin/splint:${PATH}
# for android jni developments
export PATH=/home/wayne/CodeSourcery/Sourcery_G++_Lite/bin:${PATH}
# for android sdk
export PATH=/home/wayne/android-sdk-linux_x86-1.5_r1/tools:${PATH}
export PATH=/home/wayne/jdk1.6.0_12/bin:${PATH}
export JAVA_HOME=/home/wayne/jdk1.6.0_12
export ANDROID_JAVA_HOME=${JAVA_HOME}
export ANDROID_HOME=/home/wayne/android-sdk-linux_x86-1.5_r1
# for symbian s60 v3 dev
export PATH=/home/wayne/epoc_sdk/s60_31_fp1/tools_wrapper:$PATH
export EPOCROOT=/home/wayne/epoc_sdk/s60_31_fp1/
# for android platform codes
export ANDROID_PRODUCT_OUT=/home/wayne/works/android_source/out/target/product/generic
# for android ndk
export ANDROID_NDK_ROOT=/home/wayne/android-ndk-1.5_r1/
# for apache ant
export PATH=/usr/local/apache-ant-1.7.1/bin:${PATH}
export ANT_HOME=/usr/local/apache-ant-1.7.1
# for splint
export LARCH_PATH=/usr/local/share/splint/lib
export LCLIMPORTDIR=/usr/local/share/splint/import
# for git
export PATH=/usr/local/bin:${PATH}
(2)建立JniTest的项目
具体的命令行建立过程,请参考第一篇文章的方法。
在JniTest的目录下建立一个叫做“jni”的子目录,这个目录将用来存放.c的文件。
(3)编写jni模块的java调用类
这是必然的了,jni嘛,一定要有调用者才能够工作在src的最内层目录里面添加一个叫做JniModule.java的原文件,看上去如下所示:
public class JniModule {
static {
System.loadLibrary("aaaa") ;
}
public native static int jni_add(int a, int b) ;
}
注意,偶们最终会生成一个叫做libaaaa.so的arm兼容的二进制动态库,但是在使用System.loadLibrary动态载入的时候,只需要填写lib和.so之间的名字aaaa即可,在此实验的功能仅仅是两个数字a和b的求和计算以及如何在jni的c语言模块中把log日志打印到logcat中。
在JniTest.java中,偶们可以如下调用这个类:
public void onClick(View v) {
String ss ;
int a = 3 ;
int b = 4 ;