当前位置:操作系统 > 安卓/Android >>

JNI调用机制

JNI的简单介绍
 
Java Native Interface (JNI)是java本地调用接口,所谓的native就是调用c/c++的程序。
java调用C语言的情况一般有三种:
调用驱动。由于操作系统提供的驱动一般都是C接口,Java语言并不具备操作这些驱动的能力。
对于计算量比较大,处理数据比较多的模块,java的效率没有C高,所以希望用C去完成。
对于某些功能模块,可能Java和C的效率差不多,但是C已经写好了,就不想用Java重写了。
从程序的角度来说,主要关注两种情况:
java访问C
C访问java
对于理解JNI的调用实现,对于理解Android的源码有帮助,因为Android系统中大量的使用了JIN。
 
Java访问C
 
    任何语言直接的交互都必须遵循一定的规则或者协议,只有这样双方才能理解各自的意图。
java中定义native函数,对于native函数只需要声明,具体实现由C去实现。也就是说,native函数的
实现与声明是分离的,java负责声明,C负责实现。所以java在编译是不会关心具体实现,编译时就不会出错。
 
    如何调用的呢?在调用之前java是不会关心是否已经实现,只有在调用native方法的时候,去找C生成的动态库,如果
找不到动态库,那么native方法就会报错。
 
   java如何找到C的函数的呢?
 
    java的native函数和C中的函数存在一种映射关系,这个映射关系就是遵循的一种协议或者称为规则。
    比如,Framework中AssetManager类中声明了:
    
    private native final void init()
 
    该方法在C中对应的是:
   
   static void android_content_AssetManager_init(JNIEnv* env, jobject clazz)
 
   从上面的对应关系可以看出,在C中的规则就是,包名+类名+方法名,并且中间用下划线分割
   第一个参数env,是JNIEnv对象,该对象代表一个Java虚拟机所运行的环境,通过它可以访问JVM内部的各种对象;
    第二个参数jobject和是调用该函数的对象,上面的例子指的就是AssetManager对象;每个这样的C函数的参数至少
    有这两个参数,如果native函数里有多个参数,依次在后面排列,java的数据类型和JNI中的数据类型对应关系,
自己可以去网上查询一下。
 
   当java调用native函数时,编译器会向native引擎传递调用者的包名,函数名及参数类型,native引擎
根据这些信息决定应该调用具体的的哪个函数。
 
在android中,native引擎中的AndroidRuntime类提供了一个registerNativeMethods()函数,通过此函数
定义java方法和C函数的映射关系。
 
生成 .h文件
 
java 的native方法和JNI中的c中的函数的对应的定义文件头文件(.h文件)可以手工写,有些麻烦
并且还容易出错,java提供了一个javah工具,通过该工具可以从一个java文件自动生成相应的
投文件,网上查询一下具体用法。
    
接下来就是根据头文件去实现相应的C代码,然后生成动态库。
 
如果想在java中调用native方法,需要使用在调用的代码前面使用System.loadLibrary("lib_name")去
装载该动态库。
 
 
C访问java
 
  虽然C访问java的情况不多见,不过也是能遇到的。
 
 由于java中的函数在native引擎中并没有直接的函数指针,java函数只能由java引擎去执行,而不是C。所以访问
java不能通过指针,只能通过参数接口。
 
 java访问c的时候,把类名,函数名,参数类型传递给native引擎,然后由native引擎处理C函数,同理,
C调用java时,也需要把想要访问的类名、函数名、参数传递给java引擎。
 
按照如下步骤:
 
获取java对象的类
jclass cls = env->GetObjectclass(jobject);
jobject就是需要调用的谁的方法,java对象在JNI中的表示。
获得java方法的Id值
jmethodId mid = env->GetMethodId(cls, "method_name", "([Ljava/lang/String;)V");
第一个参数是java对象对应的类
第二个参数是java中的方法名称
第三个参数是数据类型和返回值
解释一下第三个参数:
参数在括弧中,返回值在括弧外, ([Ljava/lang/String;)V" : [Ljava/lang/String代表参数类型,如果是类包名用
“/”分开,并且前面加上又给大写的L。
java和native数据类型对应表
java类型 native类型
boolean Z
byte B
char C
double D
float F
int I
long L
Object 'L' + '包名'+';'
short S 这个参数可以自己手工写,还是那样容易出错,java提供了一个工具用来查看参数签名,使用javap工具可以自动生成,自己从网上查询一下即可。
调用函数
env->CallXXXMethod(jobject, mid, ret);
 
其中XXX是不同的类型,包括:Void,Oject,Boolean,Byte,Char,Short,Int,Long,Float,Double
第一个参数是调用的类的对象
第二个参数是上一步获得的方法的id
第三个参数数是返回值
上面是调用方法,调用java的变量也类似:
 
获取java对象的类
jclass cls = env->GetObjectclass(jobject);
获得变量的id
jfiledId fid = env->GetFiledId(cls, "fileName", "I");
获取变量值
value = env->GetXXXField(env, jobject, fid);
 
在C中使用持久对象和保持持久对象
 
 C中本身无法保存持久对象,把持久对象保存到一个int类型的变量上,使用的时候再强制转型成对象。
补充:移动开发 , Android ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,