AndroidNDKJNI入门进阶•AndroidNDK简介•下载和安装AndroidNDK•配置AndroidNDK的开发环境•AndroidJNI接口设计•编写AndroidNDK程序的步骤•配置Android.mk文件•AndroidNDK定义的变量•AndroidNDK定义的函数•配置Application.mk文件•关于JNI的一些相关操作•JNI调用Java方法•本章小结AndroidNDK简介AndroidNDK(NativeDevelopmentkit)是一套允许开发人员将本地代码嵌入Android应用程序的开发包。众所周知,Android应用程序运行在Dalvik虚拟机上。而NDK允许开发人员将Android应用程序中的部分功能(由于NDK只开发了部分接口,因此,无法使用NDK编写完整的Android应用程序)用c/c++语言来实现,并将部分c/c++代码编译成可直接运行在Android平台上的本地代码。这些本地代码以动态链接库(lib…so)的形式存在。NDK的这个特性既有利于代码的重用,也可以在某种程度上提高程序的运行速度。AndroidNDK简介NDK由如下几部分组成:•提供了一套工具集,这套工具集可以将c/c++源代码生成本地代码。•用于定义NDK接口的C头文件(*.h)和实现这些接口的库文件。•一套编译系统。可以通过非常少的配置生成目标文件。下载和安装AndroidNDKAndroidNDK需要一个c/c++编译环境才能使用。因此不仅要安装AndroidNDK,还需要安装相应的c/c++环境。如果在Linux下使用AndroidNDK,因为一般Linux安装包都自带了c/c++编译环境,所以只需要在安装Linux时选中相应的开发工具即可。如果在windows下使用AndroidNDK,仍然需要使用Linux环境的c/c++编译器来生成lib…so文件。这是Linux/UNIX下的动态链接库文件,相当于window下的dll文件。文件名必须以lib开头,文件扩展名必须是.so。例如libLog.so、libImage.so等。可以从如下地址下载AndroidNDK的最新版本下载后,将AndroidNDK的压缩包解压缩即可。配置AndroidNDK的开发环境•设置AndroidNDK的路径在Linux环境下(fedora)安装了android-ndk-r5b-mips-linux(用于板子上生成so)和android-ndk-r5c(用于手机或模拟器上生成so),现在主要用android-ndk-r5c为大家讲解。在根目录下vi.bash_profile,在该文件上更改路径如下:#.bash_profile•#Getthealiasesandfunctions•if[-f~/.bashrc];then•.~/.bashrc•fi•#Userspecificenvironmentandstartupprograms•PATH=$PATH:$HOME/bin:/home/xiongwq/ndk-x86/android-ndk-r5c•exportPATH•ANDROID_NDK_ROOT=:/home/xiongwq/ndk-x86/android-ndk-r5c•exportANDROID_NDK_ROOT配置AndroidNDK的开发环境•安装AndroidNDK开发环境在完成第一步后,重启linux系统。然后在终端控制台中执行下面的命令进入AndroidNDK中的根目录:cd$ANDROID_NDK_ROOT然后运行ndk里面自带的samples/hello-jni的例子,输入ndk-build–B命令,如果出现图片所示的内容,说明AndroidNDK开发环境已经安装成功。AndroidJNI接口设计AndroidNDK应用程序的接口实际上就是在JNI(JavaNativeInterface)规范中定义的接口。JNI规范中定义了Java调用动态链接库(*.dll或*.so文件,由于Android是Linux内核的操作系统,因此只有*.so文件)的约定。这里的接口就是指函数,包括函数名称、函数参数个数、函数参数类型及函数返回值的类型。AndroidJNI接口设计举个简单的例子,看一下hello-jni.c文件中的c语言函数,代码如下:JstringJava_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv*env,jobjectthiz){return(*env)-NewStringUTF(env,“HellofromJNI!”);}上面的代码中的函数从表面上看只是一个普通的c语言函数,但这个函数和普通的c语言函数有如下3点不同:AndroidJNI接口设计•类型不同该函数的返回值类型和参数类型都是在JNI的头文件中定义的类型(如jstring、jobject等)。这些类型与Java中的数据类型对应,例如,jstring对应Java中的String;jobject对应Java中的Object。在定义被Java调用的JNI函数时必须使用这些类型,否则Java无法成功调用这些函数。•参数env和thiz上面的函数有两个参数:env和thiz。这两个参数必须包含在JNI函数中,而且必须是头两个参数。其中env表示JNI的调用环境,thiz表示定义native方法的Java类的对象本身。AndroidJNI接口设计•JNI函数名不同打开hellojni工程中的HelloJni.java文件,看到在HelloJni类中定义的native方法是stringFromJNI,该方法调用了上面的C语言函数。但上面的C语言函数名却为Java_com_example_hellojni_HelloJni_stringFromJNI。从这个函数名可以看出,HelloJni类中的native方法stringFromJNI是该函数名的结尾部分。前面有一个由“_”分隔而成的组合前缀。其中Java是固定的,而com_example_hellojni_HelloJni是HelloJni类的全名(包名+类名),只是将“.”换成了“_”。从这一点看出,一个完整的JNI函数名由3部分组成:Java、定义native方法的类的全名、实际的函数名。这3部分用“_”进行连接。AndroidJNI接口设计总结上面3个与普通c语言函数的区别也是编写JNI函数的关键。不过开发人员也不需要太关注这3点,因为一般在编写JNI函数之前,需要先编写一个调用JNI函数的Java类(定义native方法的Java类),然后使用JDK中的javah.exe命令自动生成定义JNI函数的C语言头文件(*.h文件)。该文件中定义的函数会完全采用上面的3个规则,开发人员只需要将这个函数复制到C语言源文件(*.c)中,然后编写具体的实现即可。编写AndroidNDK程序步骤•创建一个eclipseAndroid工程•创建一个定义native方法的Java类,并在该类中定义native方法。方法名就是之前介绍的函数名的3部分。•使用javah命令根据这个Java类生成c语言头文件。•根据c语言头文件中定义的JNI函数编写c语言源文件(*.c文件)。函数的实现过程要根据具体的业务逻辑而定。•在AndroidNDK安装目录\sources\samples目录中建立一个子目录(也就是保存c语言源文件的目录),然后将c语言源文件复制到该目录中。•在上一步建立的目录中创建一个Android.mk文件,也可以将hello-jni目录中的Android.mk文件复制到该目录下。编写AndroidNDK程序步骤•利用ndk命令生成so库启动控制台,输入cd$ANDROID_NDK_ROOT命令进入AndroidNDK的根目录,并使用ndk-build-B命令编译c语言源文件。如果编译成功,我们会在上一步建立的project目录中看到一个libs目录。进入该目录中的armeabi目录,会看到一个lib*.so文件。然后直接将libs目录复制到EclipseAndroid工程的根目录(与src目录平级)。编写AndroidNDK程序步骤•总结上面的7步描述了编写和调用ndk程序的完整步骤,接下来就是使用Java来调用native方法了。为了能更充分地理解编写AndroidNDK程序的步骤和具体实现细节,编写了一个将指小写字母转换成大写字母的例子。在该例子中,转换部分使用JNI函数编写。编写AndroidNDK程序步骤工程目录:src\ch16_lowertoupper•创建一个EclipseAndroid工程,工程名为ch16_lowertoupper.•创建一个LowerToUpper类。在该类中定义了native方法,代码如下:•packagenet.blogjava.mobile.jni;•importandroid.app.Activity;•importandroid.os.Bundle;•importandroid.widget.TextView;publicclassLowerToUpperextendsActivity{•/**Calledwhentheactivityisfirstcreated.*/•@Override•publicvoidonCreate(BundlesavedInstanceState){•super.onCreate(savedInstanceState);•/*CreateaTextViewandsetitscontent.thetextisretrievedbycallinganativefunction.*/•TextViewtv=newTextView(this);•tv.setText(convert('a')+);•setContentView(tv);•}•/*Anativemethodthatisimplementedbythe•*‘LowerToUpperi'nativelibrary,whichispackagedwiththisapplication.*/•publicnativecharconvert(charch);•/*thisisusedtoloadthe‘LowerToUpper'libraryonapplicationstartup.*/•static•{•System.loadLibrary(LowerToUpper);•}•}编写AndroidNDK程序步骤在编写上面代码时应注意如下两点:1.native方法为convert。该方法的参数ch为待转换的字母。2.本例使用的lib*.so文件名是libLowerToUpper.so。必须在static块中使用System.loadLibrary方法加载该文件,但指定的文件名是不包括lib和.so部分的。编写AndroidNDK程序步骤•生成C语言头文件打开windowscmd命令窗口,进入ch16_lowertoupper工程目录\bin目录,并输入如下命令生成C语言头文件:javah–jninet.blogjava.mobile.jni.LowerToUpper在执行完上面的命令后,会在当前目录生成一个net_blogjava_mobile_jni_LowerToUpper.h文件,内容如下:编写AndroidNDK程序步骤/*DONOTEDITTHISFILE-itismachinegenerated*/#includejni.h/*Headerforclassnet_blogjava_mobile_jni_LowerToUpper*/#ifndef_Included_net_blogjava_mobile_jni_LowerToUpper#define_Included_net_b