果の狸

Android小白成长之初级篇:NDK配置

AndroidNDKNative Development Kit)实际上属于JNIJava Native Interface)的一部分,JNI就是允许Java代码和其他语言写的代码进行交互。Android基于Java平台,自然也是支持这个特征了,因此Android NDK就是支持在Android平台上使用Java语言调用采用C/C++编译的库文件的一系列工具。

1.下载:
NDK的下载页面为:http://developer.android.com/sdk/ndk/index.html,当前最新版本为r8,如下图所示:

<IGNORE_JS_OP>peixun_1.PNG

 

前面我们说过Android SDK支持三种平台,这里相应的Android NDK也是分为三种平台WindowsLinux Mac OS X。其中Mac OS X 后面的括号中给出Intel。说明仅支持英特尔的硬件平台,即在英特尔的电脑上安装的Mac OS X系统才能使用该NDK,早期的苹果电脑使用的是非英特尔的硬件平台,所以这个对于苹果电脑用户而言需要是注意的。Linux平台后面的括号中给出x86,说明只支持运行在x86体系上的Linux平台。当然Windows平台早期还支持Alpha处理器,后来的版本就只支持x86体系了。鼠标点击链接后就开始下载相应的版本了。对Windows平台有特殊情结的可以使用Windows版本的NDK,当然需要Linux系统的模拟环境,最常用的就是Cygwin。苹果迷就直接用Mac OS X版本的NDK就行了。大多数用户,我们还是推荐使用Linux平台的NDK,下面我们就以Linux平台的NDK来配置NDK的开发环境。对于Linux环境,Google官方推荐平台是Ubuntu,我们这里用的是CentOS6.2版本,Linux不同版本对于开发来说大同小异,下面我们就以CentOS6.2来进行配置NDK。
 
2.NDK的目录结构说明
下载Linux平台的NDK文件解压缩后看到的目录结构如下所示:
<IGNORE_JS_OP>peixun_2.PNG

build:进行编译时用到的脚本和一些补丁
docs:有关NDK的相关文档
platforms:针对不同Android发行版本进行编译用到的头文件和库文件
prebuilt:一些小工具,用于真正编译前的预处理阶段
samples:给出了一些NDK的例子
sources:一些在编译过程中被脚本或其它工具用到的头文件和库文件
tests:执行NDK的自动化测试,确保NDK正式发布后能够正常运行
toolchains:具体进行编译时的工具,包括对三种架构armmipsx86的支持
2.NDK的目录结构说明
下载Linux平台的NDK文件解压缩后看到的目录结构如下所示:
<IGNORE_JS_OP>peixun_2.PNG

build:进行编译时用到的脚本和一些补丁
docs:有关NDK的相关文档
platforms:针对不同Android发行版本进行编译用到的头文件和库文件
prebuilt:一些小工具,用于真正编译前的预处理阶段
samples:给出了一些NDK的例子
sources:一些在编译过程中被脚本或其它工具用到的头文件和库文件
tests:执行NDK的自动化测试,确保NDK正式发布后能够正常运行
toolchains:具体进行编译时的工具,包括对三种架构armmipsx86的支持
本版本的NDK中有多个例子,我们简要说明一下这些的例子的具体作用,便于大家对这些例子有一个初步认识,至少知道每个例子引入的目的。
bitmap-plasma如何在NDK中使用bitmap的例子,早期的NDK版本不能直接使用bitmap,后来的版本中增加了对bitmap的支持。
hello-gl2NDK中如何使用OpenGLES的运用
hello-jni最基本的NDK使用方式,通过NDK获取字符串然后在Android应用中显示出来
hello-neonNDK中有关neon的优化
module-exports多个库的调用方式。foo被编译为静态库,bar被编译为动态库并调用了库foozoo被编译为动态库并调用了库bar
native-activity完全用NDK实现整个Android程序
native-audioNDK中有关音频的操作
native-mediaNDK中对视频的操作
native-plasma完全用NDK实现整个Android程序并且提供了涉及plasma的优化
san-angeles移植到Android平台的OpenGL ES的例子
test-libstdc++C++的支持,但并非支持C++的全部特征

two-libs:两个库的使用,first为静态库,second为动态库,并且second库调用first

 

 

4.C语言代码实现:

我们以NDK开发包中自带的例子hello-jni来进行演示和说明。进入jni目录下,运行ndk-build就可以进行编译了,如下图所示:
<IGNORE_JS_OP>peixun_6.PNG

 

编译后得到库libhello-jni.so,以lib字符作为前缀,so作为后缀。实际在程序加载时要去掉这两部分,即文件名称在lib.so中间的部分为真正的库名称。需要说明的是和libhello-jni.so文件在一起的还有两个文件gdb.setupgdbserver,这两个是用于NDK调试的,在最后正式发布apk文件时这两个文件时不需要的。具体NDK的调试方法,我们在后面会将,现在在这里暂时不讲。


Eclipse中加载该例子,由于该例子最开始不是Eclipse项目,需要创建新项目,选择从源代码方式创建,如下图所示:
<IGNORE_JS_OP>peixun_7.PNG



创建后,如下图所示:
<IGNORE_JS_OP>peixun_8.PNG

 

Jni目录下文件hello-jni.c如下图所示:

<IGNORE_JS_OP>peixun_9.PNG

2012-6-20 19:03 上传
下载附件 (9.13 KB)
 



从上面两幅图可以看到,hello-jniJava环境中的类HelloJni中声明的函数名为stringFromJNI,前面要用到修饰符native,类HelloJni所在的包名为com.example.hellojni,需要用函数System.loadLibrary加载liblibhello-jni.so,采用static修饰符确保只加载一次。然后按正常函数使用就行了。而在NDK这边,即C语言这层,函数命名要遵循Java___函数的命名方式,中间用下划线_进行连接,如上面所示,在包com.example.hellojni中的类HelloJni声明为stringFromJNI,则在C/C++文件中定义的函数名为Java_com_example_hellojni_HelloJni_stringFromJNI,注意区分大小写。函数可以传参数,本例子中没有传入参数,只是有返回参数,返回参数的数据类型为jstring,即Java中的String类型。对于C/C++中与上面的Java环境打交道的数据类型可以参照JNI,具体可访问官方网站http:// java.sun.com/docs/books/jni/。对于函数Java_com_example_hellojni_HelloJni_stringFromJNI中的两个参数envthiz是系统必须的,如果需要输入参数,必须再增加参数。这个例子运行的结果就是在文本控件(TextView)上面显示Hello from JNI by C !,如下图所示:
<IGNORE_JS_OP>peixun_10.PNG

 

 

 

5.C++语言代码实现:

我们对上面的例子hello-jni稍微改动,改名为hello-jni-ex,为了以示区别把类名和包名也稍微修改了一下。 C++语言这种方法灵活性更好,当然,具体采用哪一种根据自己的喜好选择。Hello-jni-ex如下图所示:
<IGNORE_JS_OP>peixun_11.PNG



程序运行结果如下图所示:

<IGNORE_JS_OP>peixun_13.PNG



有关NDK的实现部分如下图所示:

<IGNORE_JS_OP>peixun_12.PNG



函数registerNativeMethodsregisterNatives,以及JNI_OnLoad里边的东西,我们这边暂时不讲述,后面进一步讲述NDK开发时再详细讲述。我们真正自己定义的NDK函数是testStringFromJNI,这个函数名称不需要和上层Java类中的native声明函数保持一致。需要保持一致的是对应的包名和类名,这个在变量classPathName中定义了,定义为:com/example/hellojniex/HelloJniEx。在JNINativeMethod类型的数组filterMethods中建立上层Java层面函数调用和NDK层函数之间的映射关系。第一个变量stringFromJNI为在Java层进行调用的函数名称,第二个变量()Ljava/lang/String;为函数的参数和返回值,第三个变量(void*)testStringFromJNINDK层函数名称(实际就是函数指针)。第一个变量和第三个变量都很容易理解,第二个变量理解起来麻烦一些。()中表示的是函数的参数类型,()后面表示的是函数的返回参数类型。例如本例中的()Ljava/lang/String;就表示没有参数,返回String类型。类型说明如下:

 

字符 Java类型 C类型

V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
数组则以"["开始,用两个字符表示
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray boolean[]

上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾中间是用"/"隔开的包及类名。而其对应的C函数名的参数则为jobject. 一个例外是String类,其对应的类为jstring

 

Ljava/lang/String;String jstring
Ljava/net/Socket; Socket jobject

 

 

 

如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。

 

例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"
 
6.总结:
本次有关NDK的配置到此就讲完了,看完本文后,用户应该能够基本掌握NDK的配置和开发。对于进一步有关NDK的开发,我们将在后面的高级开发中详细讲解。

 

posted on 2012-07-15 13:07  果の狸  阅读(834)  评论(0)    收藏  举报

导航