Android中jni(NDK)的配置

-- elipse下android jni编程的配置
【官网】:http://developer.android.com/sdk/ndk/index.html

应用场景

在遇到如下场景时,我们需要使用elipse进行android jni编程.. 1)重用现有的代码,比如开源的C/C++组件,自己公司的旧c++代码在Android中的重用。 2)数据安全,比如将Http的请求加密和解密算法放在NDK中去实现,这样可以提高应用的安全。 3)提升性能,由于Android设备制造商在手机中给每个应用分配了可用的最大RAM,有时候为了性能考虑,可以通过Native代码向系统来“借”一些内存,尽量少的使用系统分配给应用的内存。

基础资源

jdk_1.8.0_91,eclipse,android sdk,android-ndk-r12b,cygwin(用于模拟linux环境)

使用须知

1.)ndk6以前的版本,需要单独安装cygwin. 2.)C/C++的编译环境需要Linux的gcc,所以在Windows环境下用Cygwin来模拟Linux.

配置步骤

步骤1)从(https://developer.android.com/ndk/downloads/index.html#rel)下载ndk-r12b//如果无法下载,可能需要翻墙,或者从别处下载.

步骤2)解压解压android-ndk-r12b-windows-x86_64.zip到一个目录:

D:Androidandroid-ndk-r12b


步骤3)在eclipse中设置ndk目录.


打开Eclipse->windwos->preference->Android->NDK,
把D:Androidandroid-ndk-r12build
注意这里别漏了build,不然你会发现Eclipse提示Invalid path for NDK这点很奇怪,官网都说是解压目录的路径,但是实际要进到build的目录才行


步骤4)右键打开【我的电脑】->点击【属性】->【高级系统设置】->【环境变量】->【系统变量】:

【新建】:  NDK_ROOT   D:Androidandroid-ndk-r12b //这是一个键值对(不含build的目录).

步骤5)在上述【系统变量】中选择【path】并双击.

追加: ;%NDK_ROOT%

步骤6)检查ndk是否配置成功.

直接在cmd窗口中输入 ndk-build  //看看有没有什么版本提示之类的  还是提示出错.

步骤7)导入jni项目(已存在jni项目,自动忽略该步骤).

解压文件并导入工程到Eclipse,右键工程--->Android Tools--->Add Native Support--->finish

步骤8)命令行编译,下面是手工命令编译.

cd 包含jni代码jni项目目录

ndk环境变量目录/ndk-build  //也可以在cmd进入项目目录后,直接: ndk-build






常见问题

快速入门

------关于jni头文件,数据类型,签名等.  

javac 编译java源文件生成.class文件

由于JNI对应的头文件由javah工具根据对应的.class文件生成,所以在进行JNI编程之前,写好Java代码后需要先编译,在使用javah生成对应的头文件

javah -jni自动生成头文件

举例说明:

生成普通的JNI头文件

javah -classpath path -jni -d outputdirpath com.mrljdx.JavaNativeCode

 

Java函数中包含Android相关的参数代码,则需要在classpath中添加android.jar包的绝对路径地址

javah -classpath path:$ANDROID_HOME/path/android.jar -jni -d outputdirpath com.mrljdx.JavaNativeCodeWithAndroid

 

javap -s -p 查看函数签名.

-s: 显示签名(只显示public类型的签名) -p:显示所有函数、成员变量的签名

举例说明:

javap -classpath pacakage_path_dir -s -p com.mrljdx.JavaCode

JNI数据类型和类型签名

数据类型

JNI的数据类型包括:基本类型和引用类型。这一点和Java的语言特性一致,基本类型包括jbooleanjcharjintjlongjbytejshortjfloatjdoublevoid,与Java类型的对应关系如下:

JNI类型

Java类型

描述

jboolean

boolean

无符号8位整型

jbyte

byte

有符号8位整型

jchar

char

无符号16位整型

jshort

short

有符号16位整型

jint

int

32位整型

jlong

long

64位整型

jfloat

float

32位整型

jdouble

double

64位整型

void

void

无类型

JNI中引用类型主要有类、对象和数组,这点也上符合Java的语法规范,对应的关系如下:

JNI 类型

Java引用类型

描述

jobject

Object

Object类型

jclass

Class

Class类型

jstring

String

String类型

jobjectArray

Object[]

对象数组

jbooleanArray

boolean[]

boolean数组

jbyteArray

byte[]

byte数组

jcharArray

char[]

char数组

jshortArray

short[]

short数组

jintArray

int[]

int数组

jlongArray

long[]

long数组

jfloatArray

float[]

float数组

jdoubleArray

double[]

double数组

jthrowable

Throwable

Throwable

JNI类型签名

JNI的类型签名标识了一个特定的Java类型,这个类型可以是类和方法,也可以是数据类型。

类型签名
类的签名采用”L+包名+类名+;”标识,包名中将.替换为/即可。
比如String类的签名:
Ljava/lang/String;
注意末尾的;属于签名的一部分。
再比如AndroidContext类的签名:
Landroid/content/Context;

基本数据类型签名
基本数据类型的签名采用一系列大写字母来标识,如下:

Java类型

签名

boolean

Z

byte

B

char

C

short

S

int

I

long

J

float

F

double

D

void

V

可以发现除了 long基本数据类型的签名为J之外其他的都比较容易辨识,估计是由于之前的类类型的签名开头为L+包名+类名+;设计者为了区分所以签名为J

数组的类型签名
数组的类型签名比起类类型和基本数据类型的要稍微复杂一点,不过还是很好理解的。对于数组来说,它的签名为[+类型签名,举例说明:
String[] 数组类型对应的签名:
[Ljava/lang/String;
可以发现,就是在String的类签名前加了个[
同理基本数据类型签名int[]的签名:
[I
注意这里基本类型后面是不带分号的。
那么多维数组呢?可以类推,int[][] 的签名为[[I ,而String[][]的签名为[[Ljava/lang/String;

方法的签名
JNI中会经常需要在C/C++代码中调用Java的函数,这时候就会用到方法的签名。方法的签名为(+参数类型签名+)+返回值类型签名,比如:
方法: boolean login(String username,String password)的方法签名如下:
(Ljava/lang/String;Ljava/lang/String)B 如果这里不理解的话,请再去看看之前关于基本类型,类类型的签名部分内容。

小技巧:使用 类似于javap -classpath pathdir -s -p com.sample.JavaCode javap -s -p 命令也可以帮助查看一些类中各种方法和成员变量的签名。

【JNI相关命名解释

函数名的格式遵循规则:Java_包名_类名_方法名

JNIEXPORTJNICALLJNIEnvjobject 都是JNI标准中所定义的类型或者宏

JNIEnv * : 指向JNI环境的指针,可以通过JNIEnv * 访问JNI提供的接口方法

JNIEXPORTJNICALL:是jni.h中所定义的宏。

注:JNIEnv * 可以简单的理解为JavaC/C++ 之间相互调用的桥梁,我们可以通过JNIEnv * 调用C/C++定义的方法,也可以在C/C++中通过JNIEnv * 来调用Java类中的方法。下面将会讲到C/C++中调用Java的方法,注意JNIEnv *的作用。


------一般流程及示例.

Java中定义一个静态方法供JNI调用,注意要是静态的。

JNI中利用env来调用Java中定义的静态方法

调用声明好的静态方法

5.1.1)android定义静态方法:

//对应包名:com.mrljdx.jni.HelloJNIpublic static void helloJava() {    

    System.out.println("Hello JavaCode");

}

5.1.2)JNI中声明静态方法:

static void static_helloJava(JNIEnv *env){

      jclass clazz = env->FindClass("com/mrljdx/jni/HelloJNI");

      jmethodID mid = env->GetStaticMethodID(clazz, "helloJava", "()V");

      env->CallStaticVoidMethod(clazz, mid);

}

5.1.3)JNI调用声明好的静态方法:

static_helloJava(env);

 

注:JNIEnv * 可以简单的理解为JavaC/C++ 之间相互调用的桥梁,我们可以通过JNIEnv * 调用C/C++定义的方法,也可以在C/C++中通过JNIEnv * 来调用Java类中的方法。下面将会讲到C/C++中调用Java的方法,注意JNIEnv *的作用。



参考资料