继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Java中JNI的使用

holdtom
关注TA
已关注
手记 1842
粉丝 240
获赞 991


JNI基础篇

Java 通过 JNI 调用本地方法的过程大致是:

写一个 Java 类,在其中声明对应要调用的 native 方法,用 native 关键字修饰。 比如 private static native int native_newInstance();

通过 javah 命令生成 Java 类对应的 C/C++ 头文件。javah -encoding utf-8 -cp src com.young.soundtouch.SoundTouch;

在 C/C++ 中实现头文件中声明的函数;

编译 C/C++ 代码为动态库(Windows中的dll、Linux/Android 中的 so、MAC OSX 中的 dylib);

在 Java 代码中加载动态库,即可像调用 Java 方法一样,调用到 native 函数。

其中第3步在 Java 1.2 中增加了 JNI_OnLoad 方法之后有另一种实现方式(后面说)。

javah 生成的头文件大致是这样的:

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class com_young_soundtouch_SoundTouch */

 

#ifndef _Included_com_young_soundtouch_SoundTouch

#define _Included_com_young_soundtouch_SoundTouch

#ifdef __cplusplus

extern "C" {

#endif

#undef com_young_soundtouch_SoundTouch_SETTING_USE_AA_FILTER

#define com_young_soundtouch_SoundTouch_SETTING_USE_AA_FILTER 0L

    /*

    * Class:     com_young_soundtouch_SoundTouch

    * Method:    native_getDefaultSampleElementSize

    * Signature: ()I

    */

    JNIEXPORT jint JNICALL Java_com_young_soundtouch_SoundTouch_native_1getDefaultSampleElementSize

        (JNIEnv *, jclass);

#ifdef __cplusplus

}

#endif

#endif

文件开头就是普通的头文件,但是可以发现:

包含了 jni.h 头文件(一般位于 $JAVA_HOME/jd{jdk-version}/include 文目录内)。这是 JNI 中所有的类型、函数、宏等定义的地方。所以C/C++世界的JNI是由他制定的游戏规则。

在类中生命的常量(static final)类型会在头文件中以宏的形式出现,这一点还是很方便的。

函数的注释还是比较全的,包括了:

对应的 class

对应的 Java 方法名

对应 Java 方法的签名

方法的声明显得有点奇怪,由以下及部分组成:

JNIEXPORT这是函数的导出方式;

jint 返回值类型(jint 由 jni.h 定义,对应 int,下面具体再说吧);

JNICALL 函数的调用方式也就是汇编级别参数的传入方式;

Java_com_young_soundtouch_SoundTouch_native_1getDefaultSampleElementSize —— 超级长的函数名!!!格式是 Java_ + 类全名 + _ + JAVA 中声明的native方法名。其中会把包名中的点(.)替换成下划线(_),同时为了避免冲突把下划线替换成_1;

方法的参数,上面的这个方法在 Java 的声明中实际上是没有参数的,其中的 JNIENV 顾名思义是 JNI 环境,和具体的线程绑定。而第二个参数 jclass 其实是 Java 中的 Class。因为上面是一个 static 方法,因此第二个参数是 jclass。如果是一个实例方法则对应第二个参数是 jobject,相当于 Java中的 this。

下面在 C/C++ 中实现这个方法就行啦。但是在动手前现大致了解以下 jni.h 制定的游戏规则。

类型转换

javah 生成的头文件里面使用的类型都是 jni.h 定义的,目的是做到平台无关,比如保证在所有平台上 jint 都是32位的有符号整型。

基本对应关系如下:

引用类型对应关系:

通过表格发现,除了上面定义的 String、Class、Throwable,其他的类(除了数组)都是以 jobject 的形式出现的!事实上 jstring、 jclass 也都是 object 的子类。所以这里还是和 Java 层一样,一切皆 jobject。(当然,如果 jni 在 C 语言中编译的话是没有继承的概念的,此时 jstring、jclass 等其实就是 jobject!用了 typedef 转换而已!!)

接下来是 JNIEnv * 这个指针,它提供了 JNI 中的一系列操作的接口函数。

JNI 中操作 jobject

其实也就是在native层操作 Java 层的实例。 要操作一个实例无疑是:

获取/设置 (即 get/set )成员变量(field)的值;

调用成员方法(method)。

所以问题来了:(挖掘机技术哪家强?! o(*≧≦)ツ┏━┓ )

怎么得到 field 和 method?

通过使用 jfieldID 和 jmethodID: 在 JNI 中使用类似于放射的方式来进行 field 和 method 的操作。JNI 中使用j fieldID 和 jmethodID 来表示成员变量和成员方法,获取方式是:

1

2

3

4

jfieldID GetFieldID(jclass clazz, const char *name, const char *sig);

jfieldID GetStaticFieldID(jclass clazz, const char *name, const char *sig);

jmethodID GetMethodID(jclass clazz, const char *name, const char *sig);

jmethodID GetStaticMethodID(jclass clazz, const char *name, const char *sig) ;

其中最后一个参数是签名。 获取jclass的方法除了实用上面静态方法的第二个参数外,还可以手动获取。 jclass FindClass(const char *name) 需要注意的是name参数,他是一个类包括包名的全称,但是需要把包名中的点.替换成斜杠/。(好吧,事实上我不是太明白为啥要这么做。)

有了 jfieldID 和 jmethodID 就知道狗蛋住哪了,现在去狗蛋家找他玩 (^∇^*)

1. get:

<type> Get<type>Field(jobject , jfieldID); 即可获得对应的field,其中field的类型是type,可以是上面类型所叙述的任何一种

<type> GetStatic<type>Field(jobject , jfieldID); 同1,唯一的区别是用来获取静态成员。

2. set:

void Set<type>Field(jobject obj, jfieldID fieldID, <type> val)

void SetStatic<type>Field(jclass clazz, jfieldID fieldID, <type> value);

©著作权归作者所有:来自51CTO博客作者无鳍的鱼的原创作品,如需转载,请注明出处,否则将追究法律责任


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP