JNI调用机制

来源:互联网 发布:32团淘宝兼职 编辑:程序博客网 时间:2024/06/05 09:04

JNI调用机制

1.JNI简介

Java Native InterfaceJNI)是java本地调用接口,所谓的native就是调用c/c++的程序。

java调用C语言的情况一般有三种:

调用驱动。由于操作系统提供的驱动一般都是C接口,Java语言并不具备操作这些驱动的能力。

对于计算量比较大,处理数据比较多的模块,java的效率没有C高,所以希望用C去完成。

对于某些功能模块,可能JavaC的效率差不多,但是C已经写好了,就不想用Java重写了。

从程序的角度来说,主要关注两种情况:

1)  java访问C

2)  C访问java

对于理解JNI的调用实现,对于理解Android源码有帮助,因为Android系统中大量的使用了JNI

 

2.JNI交互规范

任何语言直接的交互都必须遵循一定的规则或者协议,只有这样双方才能理解各自的意图。

java中定义native函数,对于native函数只需要声明,具体实现由C去实现。也就是说,native函数的实现与声明是分离的,java负责声明,C负责实现。所以java在编译时是不会关心具体实现,编译时就不会出错。

 

3.JNI编写步骤

1)编译包含native接口的Java类(JniProxy)的java代码:

javac JniTest.java

2)生成对应的*.h头文件,如JniTest.h

javah JniTest.java

3使用C/C++实现本地方法,如JniTest.c/cpp

开头包含头文件jni.h:#include<jni.h>

需要实现由javah命令生成的头文件里声明的同名方法

(4)将C/C++编写的文件生成动态连接库,如JniTest.so:

ndk-build根据Android.mk编译规则生成动态库。

 

4.Java访问C/C++

Java如何调用C/C++的呢?在调用之前java是不会关心是否已经实现,只有在调用native方法的时候,去找C生成的动态库,如果找不到动态库,那么native方法就会报错。因此如果想在java中调用native方法,需要使用在调用的代码前面使用System.loadLibrary("lib_name")去装载该动态库。

javanative函数和C中的函数存在一种映射关系,这个映射关系就是遵循的一种协议或者称为规则。

    比如,FrameworkAssetManager的成员函数init是一个JNI函数:

private native final void init();

它是由C++层的函数android_content_AssetManager_init来实现的:

static void android_content_AssetManager_init(JNIEnv*env, jobject clazz) { … }

该函数定义在文件frameworks/base/core/jni/android_util_AssetManager.cpp

   从上面的对应关系可以看出,在C中的规则就是,包名+类名+方法名,并且中间用下划线分割。

第一个参数env,是JNIEnv对象,该对象代表一个Java虚拟机所运行的环境,通过它可以访问JVM内部的各种对象;

    第二个参数jobject是调用该函数的对象,上面的例子指的就是AssetManager对象;每个这样的C函数的参数至少有这两个参数,如果native函数里有多个参数,依次在后面排列,java的数据类型和JNI中的数据类型对应关系,自己可以去网上查询一下。

java调用native函数时,编译器会向native引擎传递调用者的包名,函数名及参数类型,native引擎根据这些信息决定应该调用具体的的哪个函数。

android中,native引擎中的AndroidRuntime类提供了一个registerNativeMethods()函数,通过此函数定义java方法和C函数的映射关系。

 

5.C/C++访问java

虽然C访问java的情况不多见,不过也是能遇到的。

由于java中的函数在native引擎中并没有直接的函数指针,java函数只能由java引擎去执行,而不是C。所以访问java不能通过指针,只能通过参数接口。

java访问c的时候,把类名,函数名,参数类型传递给native引擎,然后由native引擎处理C函数,同理,C调用java时,也需要把想要访问的类名、函数名、参数传递给java引擎。

5.1调用函数

1)获取java对象的类

jclass cls = env->GetObjectclass(jobject);

jobject就是需要调用的谁的方法,是newClassName创建出来的对象实例:class object =new class();

jclass/jobjectjava/对象(class/object)在JNI中的表示。

2)获得java类方法的Id

jmethodId mid = env->GetMethodId(cls, "method_name", "([Ljava/lang/String;)V");

第一个参数是java对象对应的类

第二个参数是java类的方法名称

第三个参数是java类的方法签名(数据类型和返回值)

解释一下第三个参数:

参数在括弧中,返回值在括弧外, ([Ljava/lang/String;)V"

[Ljava/lang/String代表参数类型,如果是类包名用“/”分开,并且前面加上又给大写的L

javanative数据类型映射

java类型 native类型

boolean  Z

byte         B

char        C

double    D

float        F

int            I

long        L

object    'L' + '包名'+';'

short      S

这个参数可以自己手工写,还是那样容易出错,java提供了一个工具用来查看参数签名,使用javap工具可以自动生成,自己从网上查询一下即可。

3)调用对象方法

env->CallXXXMethod(jobject, mid, param1,param2,…,paramN);

其中XXX是不同的返回值类型,包括:Void,Oject,Boolean,Byte,Char,Short,Int,Long,Float,Double

第一个参数是调用的类的对象

第二个参数是上一步获得的方法的Id

第三个以后的参数是函数所需的参数(param1,param2,,paramN

如果返回值非Void类型,需提供接收返回值变量。

5.2读写变量

上面是调用方法,调用java的变量也类似:

1)获取java对象的类

jclass cls = env->GetObjectclass(jobject);

2获得java类变量的Id

jfiledId fid = env->GetFiledId(cls, "fileName", "I");

第一个参数是java对象对应的类

第二个参数是java的变量名称

第三个参数是java类的变量类型签名

3获取对象变量值

value = env->GetXXXField(env, jobject,fid);

其中XXX是该字段(value)的类型

4)设置对象变量值

env->SetXXXField(jobject, fid,value);

其中XXX是该字段(value)的类型。

5)在C中使用持久对象和保持持久对象

C中本身无法保存持久对象,把持久对象保存到一个int类型的变量上,使用的时候再强制转型成对象。

 

参考:

《AndroidJNI学习》

《JAVA中的native关键字》

《JAVA基础之理解JNI原理》

《JNI技术实践小结--原理分析和详细步骤说明》

《在Java中调用C/C++本地库》《在C/C++中调用Java代码》

《Dalvik虚拟机JNI方法的注册过程分析》

JavaNative Interface Specification—Contents

 

0 0
原创粉丝点击