在Android开发中,由于种种原因我们需要调用C/C++代码, 这个时候就要用到Android开发者都听说过的JNI(Java Native Interface)了, 在调用JNI相关方法之前, 要对java中native关键字定义的方法进行注册, 注册方式有两种: 静态注册和动态注册, 两者优缺点如下:

此注册方法是初学者经常用到的, 比较常见, 这里简单说下流程, 1.编写一个java类,在里面加载对应的so库并且通过native关键字定义需要调用的函数

2.在命令行下输入 javac JniTest.java 生成JniTest.class文件 然后在src目录下通过 javah com.example.wenzhe.myjni.JniTest 生成 com_example_wenzhe_myjni_JniTest.h 头文件 3.将头文件拷贝到jni目录下(eclipse在src同级目录建立文件夹,Android studio 在java同级目录建立文件夹) 4.编写C/C++源代码 并把刚拷贝的头文件包含进去 ,复制头文件中函数的定义部分,并实现其中的你想要的功能

然后编写Android.mk Application.mk(Application.mk主要用来定义适应的平台,x86 arm等)

Android.mk如下:

Application.mk如下:

其中LOCAL_MODULE定义的名字就是生成的so库名字,so库前面都会有个lib前缀,上面生产的so应该为 libHelloJni.so

5.在命令行中进入jni目录,输入ndk-build 即可生产对应so库,会自动放在libs文件夹下 至此就可以运行程序了

动态注册基本思想是在JNI_Onload()函数中通过JNI中提供的RegisterNatives()方法来将C/C++方法和java方法对应起来(注册), 我们在调用 System.loadLibrary的时候,会在C/C++文件中回调一个名为 JNI_OnLoad ()的函数,在这个函数中一般是做一些初始化相关操作, 我们可以在这个方法里面注册函数, 注册整体流程如下:

示例代码如下:

上面的代码就能实现动态注册JNI了 以后要增加函数只需在java文件中声明native方法,在C/C++文件中实现, 并在getMethods数组添加一个元素并指明对应关系,通过ndk-build 生成so库就可以运行了 其中JNI版本可以在jni.h头文件中去查看支持哪些版本,一般定义在文件最后几行

动态注册中 JNINativeMethod 结构体中第二个参数需注意 括号内代表传入参数的签名符号,为空可以不写,括号外代表返回参数的签名符号,为空填写 V,对应关系入下表

签名符号

C/C++

java

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

[Z

jbooleanArray

boolean[]

[I

jintArray

int[]

[J

jlongArray

long[]

[D

jdoubleArray

double[]

[F

jfloatArray

float[]

[B

jbyteArray

byte[]

[C

jcharArray

char[]

[S

jshortArray

short[]

L完整包名加类名;

jobject

class

举个例子:

传入的java参数有两个 分别是 int 和 long[] 函数返回值为 String 即函数的定义为:String getString(int a ,long[] b) 签名就应该是 :"(I[J)Ljava/lang/String;"(不要漏掉英文分号) 如果有内部类 则用 $ 来分隔 如:Landroid/os/FileUtils$FileStatus;

当熟悉动态注册后, 动态注册无疑是注册函数的更好方式, 唯一要注意的是注册函数时, 需要额外小心, 别把类名,函数名和签名写错了, 不然loadLibraries时会导致应用Crash, 关于JNI部分知识, 我还会写一篇关于如何高效传递数据以及JNI开发过程中的一些坑的总结.