您当前的位置:首页 > 互联网教程

Android NDK Jni 开发C和C++的区别

发布时间:2025-05-14 02:25:09    发布人:远客网络

Android NDK Jni 开发C和C++的区别

一、Android NDK Jni 开发C和C++的区别

Android NDK Jni开发C和C++的区别

由于Android官方NDK例子大多使用C语言编写,所以当我们想用C++进行开发时,会出现种种错误。下面简单说说几点不同之处,代码中用红色标出了不同之处:

(1)hello.c文件。在C中没有引用,传递的env是个两级指针,用(*env)->调用方法且方法中要传入env.

3 jstring Java_com_example_Hello_hello(JNIEnv* env, jobject thiz){

4 return(*env)->NewStringUTF(env,"Hello Jni---->C!");

(2)Android.mk文件,更改后缀名为.c

8 include$(BUILD_SHARED_LIBRARY)

(1)hello.cpp文件。C++中env为一级指针,用env->调用方法,无需传入env;C++语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern"C"进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名;exter"C"{jni代码}。

6 jstring Java_com_example_Hello_hello(JNIEnv* env, jobject thiz){

7 return env->NewStringUTF("Hello Jni---->C++!");

(2)Android.mk文件,更改后缀名为.cpp

8 include$(BUILD_SHARED_LIBRARY)

二、Android NDK开发简介 NDK和SDK以及JNI有什么关系

1、NDK:Android NDK是在SDK前面又加上了“原生”二字,即Native Development Kit,因此又被Google称为“NDK”。

2、NDK全称:Native Development Kit。

3、* NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的。

4、* NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。

5、* NDK可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作。

6、NDK就是能够方便快捷开发.so文件的工具。JNI的过程比较复杂,生成.so需要大量操作,而NDK就是简化了这个过程。

7、SDK(software development kit)软件开发工具包。被软件开发工程师用于为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件的开发工具的集合。因此!Android SDk指的既是Android专属的软件开发工具包

8、Java Native Interface(JNI)标准是java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI是本地编程接口,它使得在 Java虚拟机(VM)内部运行的 Java代码能够与用其它编程语言(如 C、C++和汇编语言)编写的应用程序和库进行交互操作

9、当然一般需要进行如下操作流程:

10、1)编写java程序:这里以HelloWorld为例。为了实现在 java代码中调用c函数printf。

11、public native void testHelloWorld();

12、public static void main(String[] args){

13、new HelloWorld().testHelloWorld();

14、声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明改方法为native的,并且不能实现。

15、Load动态库:System.loadLibrary("hello");

16、这里一般是以static块进行加载的。同时需要注意的是System.loadLibrary()的参数“hello”是动态库的名字。

17、3)生成扩展名为h的头文件 javah?

18、 JNIEXPORT void JNICALL Java_HelloWorld_testHelloWorld(JNIEnv*, jobject);

19、这个h文件相当于我们在java里面的接口,这里声明了一个 Java_HelloWorld_testHelloWorld(JNIEnv*, jobject)方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致)。

20、4)编写本地方法实现和由javah命令生成的头文件里面声明的方法名相同的方法

21、JNIEXPORT void JNICALL Java_HelloWorld_testHelloWorld(JNIEnv*env, jobject obj)

22、注意代码2中的第1行,需要将jni.h(该文件可以在%JAVA_HOME%/include文件夹下面找到)文件引入,因为在程序中的JNIEnv、 jobject等类型都是在该头文件中定义的;另外在第2行需要将HelloWorld.h头文件引入。然后保存为 HelloWorldImpl.c就ok了。

23、这里以在Windows中为例,需要生成dll文件。在保存HelloWorldImpl.c文件夹下面,使用VC的编译器cl成。 cl-I%java_home%/include-I%java_home%/include/win32-LD HelloWorldImp.c-Fehello.dll注意:生成的dll文件名在选项-Fe后面配置,这里是hello,因为在HelloWorld.java文件中我们loadLibary的时候使用的名字是hello。

24、另外需要将-I%java_home%/include-I%java_home%/include/win32参数加上,因为在第四步里面编写本地方法的时候引入了jni.h文件。

25、打个不恰当比方:JNI需要自己买菜、洗菜、炒菜、煮饭,很麻烦;NDK就是宅急送,一个电话。

三、如何在Android下使用JNI

我们知道,Android系统的底层库由c/c++编写,上层Android应用程序通过Java虚拟机调用底层接口,衔接底层c/c++库与Java应用程序间的接口正是JNI(JavaNative Interface)。本文描述了如何在ubuntu下配置AndroidJNI的开发环境,以及如何编写一个简单的c函数库和JNI接口,并通过编写Java程序调用这些接口,最终运行在模拟器上的过程。

(1)从jdk官方网站下载jdk-6u29-linux-i586.bin文件。

01.$chmod a+x jdk-6u29-linux-i586.bin

$chmod a+x jdk-6u29-linux-i586.bin

03.exportJAVA_HOME=/usr/lib/java/jdk1.6.0_29

04.exportJRE_HOME=$JAVA_HOME/jre

05.exportCLASSPATH=$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH

06.exportPATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH

exportJAVA_HOME=/usr/lib/java/jdk1.6.0_29

exportCLASSPATH=$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH

exportPATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH

03.Java(TM)SE Runtime Environment(build 1.6.0_29-b11)

04.JavaHotSpot(TM) Server VM(build 20.4-b02, mixed mode)

06.用法:javah[选项]<类>

09.-classpath<路径>用于装入类的路径

10.-bootclasspath<路径>用于装入引导类的路径

12.-o<文件>输出文件(只能使用-d或-o中的一个)

13.-jni生成JNI样式的头文件(默认)

17.使用全限定名称指定<类>(例

Java(TM)SE Runtime Environment(build 1.6.0_29-b11)

JavaHotSpot(TM) Server VM(build 20.4-b02, mixed mode)

-classpath<路径>用于装入类的路径

-bootclasspath<路径>用于装入引导类的路径

-o<文件>输出文件(只能使用-d或-o中的一个)

-jni生成JNI样式的头文件(默认)

使用全限定名称指定<类>(例

如,java.lang.Object)。2.2.安装android应用程序开发环境

ubuntu下安装android应用程序开发环境与windows类似,依次安装好以下软件即可:

与windows下安装唯一不同的一点是,下载这些软件的时候要下载Linux版本的安装包。

安装好以上android应用程序的开发环境后,还可以选择是否需要配置emulator和adb工具的环境变量,以方便在进行JNI开发的时候使用。配置步骤如下:

把emulator所在目录android-sdk-linux/tools以及adb所在目录android-sdk-linux/platform-tools添加到环境变量中,android-sdk-linux指androidsdk安装包android-sdk_rxx-linux的解压目录。

02.exportPATH=~/software/android/android-sdk-linux/tools:$PATH

03. exportPATH=~/software/android/android-sdk-linux/platform-tools:$PATH

exportPATH=~/software/android/android-sdk-linux/tools:$PATH

exportPATH=~/software/android/android-sdk-linux/platform-tools:$PATH

NDK是由android提供的编译android本地代码的一个工具。

(1)从androidndk官网下载ndk,目前最新版本为android-ndk-r6b-linux-x86.tar.bz2.

01.$tar-xvf android-ndk-r6b-linux-x86.tar.bz2

02.$sudo mv android-ndk-r6b/usr/local/ndk

$tar-xvf android-ndk-r6b-linux-x86.tar.bz2

$sudo mv android-ndk-r6b/usr/local/ndk

02.exportPATH=/usr/local/ndk:$PATH

exportPATH=/usr/local/ndk:$PATH

编辑完毕后保存退出,并重启生效

01.$ cd/usr/local/ndk/samples/hello-jni/

03.Gdbserver: [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver

04.Gdbsetup: libs/armeabi/gdb.setup

05.Install: libhello-jni.so=> libs/armeabi/libhello-jni.so

$ cd/usr/local/ndk/samples/hello-jni/

Gdbserver: [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver

Gdbsetup: libs/armeabi/gdb.setup

Install: libhello-jni.so=> libs/armeabi/libhello-jni.so

我们需要定义一个符合JNI接口规范的c/c++接口,这个接口不用太复杂,例如输出一个字符串。接下来,则需要把c/c++接口的代码文件编译成共享库(动态库).so文件,并放到模拟器的相关目录下。最后,启动Java应用程序,就可以看到最终效果了。

(1)启动Eclipse,新建android工程

编辑res/values/strings.xml文件如下:

我们在主界面上添加了一个EditText控件和一个Button控件。

static表示在系统第一次加载类的时候,先执行这一段代码,在这里表示加载动态库libJNITest.so文件。

01.privatenativeString GetReply();

privatenativeString GetReply();

native表示这个方法由本地代码定义,需要通过jni接口调用本地c/c++代码。

01.publicvoidonClick(View arg0){

这段代码表示点击按钮后,把native方法的返回的字符串显示到EditText控件。

(4)编译工程,生成.class文件。

3.2.用javah工具生成符合JNI规范的c语言头文件

在终端中,进入android工程所在的bin目录

01.$cd~/project/Android/JNITest/bin

$cd~/project/Android/JNITest/bin

我们用ls命令查看,可以看到bin目录下有个classes目录,其目录结构为classes/org/tonny/jni,即classes的子目录结构是android工程的包名org.tonny.jni。请注意,下面我们准备执行javah命令的时候,必须进入到org/tonny/jni的上级目录,即classes目录,否则javah会提示找不到相关的java类。

02.$javah org.tonny.jni.JNITest

04.org org_tonny_jni_JNITest.h

执行javahorg.tonny.jni.JNITest命令,在classes目录下会生成org_tonny_jni_JNITest.h头文件。如果不进入到classes目录下的话,也可以这样:

01.$javah-classpath~/project/Android/JNITest/bin/classesorg.tonny.jni.JNITest

$javah-classpath~/project/Android/JNITest/bin/classesorg.tonny.jni.JNITest

-classpath参数表示装载类的目录。

生成org_tonny_jni_JNITest.h头文件后,我们就可以编写相应的函数代码了。下面在android工程目录下新建jni目录,即~/project/Android/JNITest/jni,把org_tonny_jni_JNITest.h头文件拷贝到jni目录下,并在jni目录下新建org_tonny_jni_JNITest.c文件,编辑代码如下:

03.#include"org_tonny_jni_JNITest.h"

06.JNIEXPORTjstring JNICALLJava_org_tonny_jni_JNITest_GetReply

08.return(*env)->NewStringUTF(env,(char*)"Hello,JNITest");

#include"org_tonny_jni_JNITest.h"

JNIEXPORTjstring JNICALLJava_org_tonny_jni_JNITest_GetReply

return(*env)->NewStringUTF(env,(char*)"Hello,JNITest");

我们可以看到,该函数的实现相当简单,返回一个字符串为:"Hello,JNITest"

在~/project/Android/JNITest/jni目录下新建Android.mk文件,android可以根据这个文件的编译参数编译模块。编辑Android.mk文件如下:

04.LOCAL_SRC_FILES:= org_tonny_jni_JNITest.c

05.include$(BUILD_SHARED_LIBRARY)

LOCAL_SRC_FILES:= org_tonny_jni_JNITest.c

include$(BUILD_SHARED_LIBRARY)

LOCAL_MODULE表示编译的动态库名称

3.5.用ndk工具编译并生成.so文件

进入到JNITest的工程目录,执行ndk-build命令即可生成libJNITest.so文件。

01.$cd~/project/Android/JNITest/

05.Install: libJNITest.so=> libs/armeabi/libJNITest.so

Install: libJNITest.so=> libs/armeabi/libJNITest.so

可以看到,在工程目录的libs/armeabi目录下生成了libJNITest.so文件。

(1)首先,我们把android模拟器启动起来。进入到emulator所在目录,执行emulator命令:

01.$cd~/software/android/android-sdk-linux/tools

02.$./emulator@AVD-2.3.3-V10-partition-size 512

$cd~/software/android/android-sdk-linux/tools

$./emulator@AVD-2.3.3-V10-partition-size 512

AVD-2.3.3-V10表示你的模拟器名称,与在Eclipse->AVDManager下的AVDName对应,-partition-size表示模拟器的存储设备容量。

(2)接下来,我们需要把libJNITest.so文件拷贝到模拟器的/system/lib目录下,执行以下命令:

01.$cd~/project/Android/JNITest/libs/armeabi/

03.$adb push libJNITest.so/system/lib

04.80 KB/s(10084 bytes in 0.121s)

$cd~/project/Android/JNITest/libs/armeabi/

$adb push libJNITest.so/system/lib

80 KB/s(10084 bytes in 0.121s)

当在终端上看到有80 KB/s(10084 bytes in 0.121s)传输速度等信息的时候,说明拷贝成功。

(3)在终端上执行JNITest程序,这个我们可以在Eclipse下,右键点击JNITest工程,RunAs->Android Application,即可在模拟器上启动程序