JNI调用机制与JNI实现
2015-08-04 17:04
295 查看
JNI调用机制
JNI第一篇此文是JNI的第二篇博客,我在之前的博客里写过如何实现一个基本的JNI,这篇文章是上一篇的升级版,详细解释了各种参数和实现方式,所以,在阅读此文前,请先看下如何实现一个基本的JNI调用,点击跳转
JNI:Java Native Interface是java本地接口。所谓的native,这里指C/C++写的底层接口。
一般在java层调用C层有这样的需求:
调用驱动,由于操作系统所提供的驱动一般都是C接口,Java语言本身不具备操作这些驱动的能力。
对于某些模块,Java的效率可能远低于C,因为,这些模块需要用C去完成。
对于某些功能模块,C层已经存在封装好的C代码,不想重复造轮子。
总之,基于某些原因,我们需要使用C代码。
Java访问C
Java中可以定义某个函数为native类型,对于native函数,只需要声明即可,因为该函数实现是native的,由相应的c去实现,Java编译器遇到native函数是,不会关心该函数的具体实现,因此,编译上不会出任何差错。关于native函数的命名规则,看一下下面两个方法:
假设我们是在Framework层中的AssetManager中调用的,
java层方法
private native final void init();
C中对应方法
static void android_content_AssetManager_init(JNIEnv* env, jobject clazz);
这种映射关系就是一种编程规范,C中方法名对应的是Java层的包名+方法名,方法中第一个参数env是一个指向java虚拟机运行环境,相当于jvm的管理者。通过它可以访问jvm内部的各种对象。第二个参数jobject是调用该函数的对象,上面的例子中,指代AssetManager对象。如果native声明中本身也有参数,那么这些参数会按顺序放在以上两个参数的后面,比如native中包含如下方法:
private native final String[] getArrayStringResource(int arrayRes);
其对应的C函数为:
static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz,jint arrayResId);
在上面的转换关系中,我们注意到native中所使用的类型和Java中不同,比如java中的int,在native方法中变为了jint,返回值String[]变成了jobjectArray,这些具体的定义实际上是在jni.h中,该文件的存放路径为:
./dalvik/libnativehelper/include/nativehelper/jni.h
./external/webkit/webkit/android/JavaVM/jni.h
./ndk/build/platforms/android-n/arch-arm/usr/include/jni.h
./ndk/build/platforms/android-n/arch-x86/usr/include/jni.h
这里的android-n的n是对应的不同版本号。
最新版5.1的源码中,jni.h的位置在:
./libnativehelper/include/nativehelper/jni.h
./external/chromium_org/third_party/npapi/npspy/extern/java
./prebuilts/ndk/n/platforms/android-n/arch-arm/usr/include/jni.h
上面的第一个n是ndk版本,第二个n是Android版本。我们可以看出根据Android版本不同,位置并不是固定的,但是大致的位置是不变的,所以我们耐心搜索下就可以找到。
如果已经写好了本地的java代码,想生成头文件,那么我们不必自己按照规范来写,可以通过命令行来执行,这个在前面的博客里已经详细说明了,需要看的童鞋请在文章开始处点击跳转。
javah -d ~/Desktop -jni com.haiii.android.client.Foo
-d的含义是输出路径,-d必须在-jni前面,-jni的意思是产生jni头文件,后面的类名是Class文件的路径。调用javah,当前路径必须在该Class包名的根目录,生成的头文件默认名称为com_haiii_android_client_Foo.h,具体细节就不在这里复述了。
在java调用native之前,我们需要使用System.loadLibrary(“lib_name”)函数来装载该库。
值得注意的是,java中不能直接访问C中的变量,因为C中的变量对于Java来说都是私有的,如果想访问某个变量,需要让C提供一个get/set方法,以达到间接访问的目的。
C访问java
这个在博客里应该见得比较少了,因为很少会发生这样的需求,但是它绝对是有用的。由于Java中的函数在native引擎中并没有直接的函数指针,Java函数只能由Java引擎去执行,而不是C,所以,C访问Java不能通过函数指针,而只能通过通用的参数接口,正如Java调用C一样。
C调用Java时,也需要把想要访问的类名,函数名,参数传递给Java引擎,步骤如下:
1.获取Java对象的类
cls = env->GetObjectClass(jobject);
其中env为Java调用C函数时的第一个参数,这意味着C调用Java函数只能在Java调用C函数中进行,否则无法获取env变量。换句话说,对于C来讲,就是”你不惹我,我不惹你“。jobject为第二个参数。cls的类型是jclass。
2.获取Java函数的id值
env->GetMethodId(cls,"method_name","([Ljava/lang/String;)V");
这个方法中第二个参数为Java中函数名称,第三个参数较为乱,它代表了Java函数的参数和返回类型,参数在()括号中,返回值在括号外。参数[Ljava/lang/String代表了String类型的参数,由于String本身是一个类,而不是Java的原子类型,所以前面加了包名,并用斜线分隔,最前面用一个中括号进行标识,后面用分号隔离。返回值V代表了void。GetMethodId中这种参数格式的定义大致如下:
boolean->Z;int->I;byte->B;long->L;char->C;double->D;short-S;float->F;Object->’L’+’packageName’+’;’这个就对应上面那个字符串
同样的,Java中提供了一个javap工具,这个工具可以让我们很清楚的找到Java函数的输入,返回参数,用法如下:
javap -s com/haiii/android/client/Foo
Foo.java的内容如下:
public class Foo { native void foo1(); native int foo2(int a,String b); }
使用javap命令,输出如下:
Compiled from "NativeClient.java" public class com.sizon.testproject.NativeClient { public com.sizon.testproject.NativeClient(); Signature: ()V native void foo1(); Signature: ()V native int foo2(int, java.lang.String); Signature: (ILjava/lang/String;)I }
这个是在cmd窗口输出的
可以清楚的看出各个函数对应的返回值和参数列表,这样我们就可以从容的写C端的调用了。
3.找到函数后,就可以调用该函数了
env->CallXXXMethod(jobject,mid,ret);
其中XXX代表了函数的返回值类型,具体包括Void、Object、Boolean、Byte、Char、Short、Int、Long、Float、Double。第一个参数是原来函数的第二个参数,即env后边的那个,第二个参数是我们获取的方法mid,第三个参数是我们自定义的存储返回值的变量。
通过上面的三步,我们就实现C中调用Java函数的目标。然后我们看一下,C中如何访问Java变量。
1.获取class,与前面相同
cls = env->GetObjectClass(jobject);
2.获取变量的id值
jfieldId fid = env->GetFiledId(cls,"filed_name","I");
参数filed_name为Java变量的名称,第三个参数为变量的类型,其格式与上面相同。
3.获取变量值
value = env->GetXXXField(env,jobject,fid);
该函数通过返回值的方式获取变量值,就是第三个参数,为返回值,前两个参数都是原装Java访问C函数的前两个参数。
到此,JNI的一些东西先讲到这里了。两篇文章结合看,对Android JNI调用这里应该就没有问题了。以后再说mk文件的事~
相关文章推荐
- Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现
- JS中的Call和apply
- Java读取properties文件中文乱码问题解决
- 【Python】阿里巴巴API签名生成
- springmvc:406
- HDU1317 XYZZY
- HBase - MapReduce - HBase 作为输入源的示例 | 那伊抹微笑
- CF 469A I Wanna Be the Guy
- mybatis 输入映射和输出映射
- SQL 优化
- 国内首个大数据平台性能标准制定完成
- java异常处理
- hdu1014
- 如何将CSV数据导入MySQL?
- Lua教程:C++和Lua相互传递数据(2)
- <meta http-equiv="X-UA-Compatible" content="IE=edge" /> 的说明 (IE兼容性模式)
- oracle常用命令总结
- href="#"和href=”javascript:"的区别
- 太行(2)
- opencv android jni 增加编译目录