/******************************************************************************************************************/

JNI(Java Native Interface(Java本地(c语言写的)接口))

一、JAVA调用C

1.Java如何调用c库的函数

1)加载C库(找到C库)

static {        /* 1. load */

System.loadLibrary("native");//加载C库导致c文件中的JNI_OnLoad被调用

}

2)找到函数(JAVA和C库之间建立映射)

(1)显示建立(一般选择这个,更灵活)

/* System.loadLibrary */

JNIEXPORT jint JNICALL

JNI_OnLoad(JavaVM *jvm, void *reserved)

{

JNIEnv *env;

jclass cls;

if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {

//先获得java程序的运行环境

return JNI_ERR; /* JNI version not supported */

}

//从而找到java程序的那个类

cls = (*env)->FindClass(env, "JNIDemo");

if (cls == NULL) {

return JNI_ERR;

}

/* 2. map java hello <-->c c_hello 最后建立对应关系,将类和C函数注册到Native中*/

if ((*env)->RegisterNatives(env, cls, methods, 1) < 0)

return JNI_ERR;

return JNI_VERSION_1_4;

}

其中methods数组定义如下,

static const JNINativeMethod methods[] = {

{"hello", "()V", (void *)c_hello},

};

关于数组成员类型中的含义如下:

typedef struct {

char *name;/* Java里调用的函数名 */

char *signature; /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型。 */

//即JAVA程序中要调用的java方法(*name指向的函数)所对应的参数和返回值,用一些符号表示,具体见图表。

(获取JNI字段描述符除了对应图标找之外,还可以先编译(java -d .JNIDemo.java)再用javah命令来生成头文件(javah -jni a.b.c.d.JNIDemo),头文件中就会含有那些描述符)

void *fnPtr; /* C语言实现的本地函数 */

} JNINativeMethod;

JNI字段描述符图表:

(2)隐式建立(自然用不到显示建立中需要做的事情,但是不灵活)

I、类a.b.c.d.JNIDemo要调用hello函数

II、C语言中要实现Java_a_b_c_d_JNIDemo_hello

//函数名固定,所以不灵活

//可以使用javah命令来获得这个复杂的函数名(头文件中已经声明好了这个函数)

III、可以用工具生成头文件

javac -d . JNIDemo.java

javah -jni a.b.c.d.JNIDemo

(3)注意,c程序中被java调用的函数,永远比对应java程序中的方法多两个参数(见javah生成的头文件,两个参数(JNIEnv *env, jobject cls))

3)调用函数

public native void hello();//使用前先使用native 关键字声明这是本地的方法(c实现的)

public static void main (String args[]) {

JNIDemo d = new JNIDemo();

/* 3. call */

d.hello();//调用

}

4)Java和C库传递数据

(1)传递基本类型数据

直接使用、直接返回

JAVA://传入123,打印返回(修改了java本地方法,自然要修改JNINativeMethod数组内容为相对应)

System.out.println(d.hello(123));

C://打印接收,返回100(可以从javah生成的头文件中获取到对应的声明来书写,去掉声明中的JNIEXPORT关键字(因为我们不需要导出,我们是注册进去的))

jint c_hello(JNIEnv *env, jobject cls, jint m)

{//使用jni.h中的类型

printf("Hello, world! val = %d\n", m);

return 100;

}

(2)传递字符串 (jni.pdf P39)

需要借用运行环境中的某些函数

JAVA:(修改了java本地方法,自然要修改JNINativeMethod数组内容为相对应)

System.out.println(d.hello("this is java"));

C:(可以从javah生成的头文件中获取到对应的声明来书写,去掉声明中的JNIEXPORT关键字)

jstring JNICALL c_hello(JNIEnv *env, jobject cls, jstring str)

{//JNICALL 可以不要,是个空的宏

//printf("this is c : %s\n", str);

//return "return from C";

const jbyte *cstr;

cstr = (*env)->GetStringUTFChars(env, str, NULL);//将传进来的String类转换为字符串

if (cstr == NULL) {//里面分配了内存,这里要判断

return NULL; /* OutOfMemoryError already thrown */

}

printf("Get string from java :%s\n", cstr);

(*env)->ReleaseStringUTFChars(env, str, cstr);//使用完了要释放

return (*env)->NewStringUTF(env, "return from c");//返回字符串

}

(3)传递数组 (jni.pdf P48)

JAVA:(修改了java本地方法,自然要修改JNINativeMethod数组内容为相对应)

int [] a = {1, 2, 3};

int [] b = null;

int i;

/* 3. call */

b = d.hello(a);

for (i = 0; i < b.length; i++)

System.out.println(b[i]);

C:(可以从javah生成的头文件中获取到对应的声明来书写,去掉声明中的JNIEXPORT关键字)

jintArray c_hello(JNIEnv *env, jobject cls, jintArray arr)

{

jint *carr;

jint *oarr;

jintArray rarr;

jint i, n = 0;

carr = (*env)->GetIntArrayElements(env, arr, NULL);//获取所有元素

if (carr == NULL) {

return 0; /* exception occurred */

}

n = (*env)->GetArrayLength(env, arr);//获取数组长度

oarr = malloc(sizeof(jint) * n);

if (oarr == NULL)

{

(*env)->ReleaseIntArrayElements(env, arr, carr, 0);//使用完了要释放

return 0;

}

for (i = 0; i < n; i++)

{

oarr[i] = carr[n-1-i];

}

(*env)->ReleaseIntArrayElements(env, arr, carr, 0);//使用完了要释放

/* create jintArray */

rarr = (*env)->NewIntArray(env, n);//构造jintArray (New<Type>Array)

if (rarr == NULL)//NewIntArray使用的参数可以搜索jni.h

{//创建失败

return 0;

}

//把oarr中所有的内容拷贝到raar中

//要设置的内容rarr,从0位开始设置,长度n,数据来源oarr。

(*env)->SetIntArrayRegion(env, rarr, 0, n,oarr);//SetIntArrayRegion使用的参数可以搜索jni.h

free(oarr);

return rarr;返回构造的jintArray类型的rarr

}

/******************************************************************************************************************/

二、C调用JAVA

1. 创建虚拟机

JavaVM* jvm;

JNIEnv* env;

/* 1. create java virtual machine */

if (create_vm(&jvm, &env)) {

printf("can not create jvm\n");

return -1;

}

jint create_vm(JavaVM** jvm, JNIEnv** env)

{

JavaVMInitArgs args;

JavaVMOption options[1];

args.version = JNI_VERSION_1_6;//表明虚拟机是哪个版本

args.nOptions = 1;

options[0].optionString = "-Djava.class.path=./"; //虚拟机查找类的路径./表示当前目录

args.options = options;

args.ignoreUnrecognized = JNI_FALSE;

return JNI_CreateJavaVM(jvm, (void **)env, &args);

}

2. 获得class

jclass cls;

/* 2. get class */

cls = (*env)->FindClass(env, "Hello");//字符串内为要找的类(Hello是要调用的类)

if (cls == NULL) {

printf("can not find hello class\n");

ret = -1;

goto destroy;

}

3. 实例化对象(非静态方法才需要实例化) : 获得构造方法(方法名为"<init>"), 构造参数, 创建对象

1)获得构造方法(方法名为"<init>")

jmethodID cid;

/* Get the method ID for the String constructor */

cid = (*env)->GetMethodID(env, cls,"<init>", "()V");//构造方法固定名为"<init>"

if (cid == NULL) {

ret = -1;

printf("can not get constructor method");

goto destroy;

}

2)构造参数(要调用的构造方法没有参数则不需要进行构造参数)

3)创建对象

jobject jobj;

jobj = (*env)->NewObject(env, cls, cid);

if (jobj == NULL) {

ret = -1;

printf("can not create object");

goto destroy;

}

4. 调用方法   : 又分为获得方法, 构造参数, 调用方法

1)获得方法

jmethodID mid;//方法ID

mid = (*env)->GetMethodID(env, cls, "sayhello_to","(Ljava/lang/String;)I");

if (mid == NULL) {//"sayhello_to"方法名,"(Ljava/lang/String;)I"sayhello_to方法参数和返回值对应的signature(用于分辨同名方法)

ret = -1;

printf("can not get method\n");

goto destroy;

}

signature(JNI字段描述符)获取可使用:

javac Hello.java

javap -p -s Hello.class(Hello.class为编译好的Hello.java)

2)构造参数(要调用的方法没有参数则不需要进行构造)

int r;//存放返回值

jstring jstr;

jstr = (*env)->NewStringUTF(env, "abcdefg");//构造传入参数,传入字符串"abcdefg"

3)调用方法

(1)静态方法:

(*env)->CallStaticVoidMethod(env, cls, mid, NULL);//NULL最后一项为要调用的方法的参数

(2)非静态方法:

int r;//存放返回值

jstring jstr;

jstr = (*env)->NewStringUTF(env, "abcdefg");//构造传入参数,传入字符串"abcdefg"

r = (*env)->CallIntMethod(env, jobj, mid, jstr);//非静态传入的是jobject jobj,传入参数jstr

printf("ret = %d\n", r);

5.读取/设置类中的属性(数据成员):

1). 获得属性ID

jfieldID nameID;

jfieldID ageID;

//get field id

nameID = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");

if (nameID == NULL) {//"name"java类中的数据成员名称

ret = -1;//"Ljava/lang/String;"signature获取见上

printf("can not get field name");

goto destroy;

}

2). 读取/设置

//get/set field

jstr = (*env)->NewStringUTF(env, "Bill");

(*env)->SetObjectField(env, jobj, nameID, jstr);//设置类中的数据成员name

//name数据成员必然属于实例化对象的,先用创建类的实例化对象jobj

获取数据成员直接把SetObjectField的Set改为Get即可

/* Read the instance field s */

jstr = (*env)->GetObjectField(env, obj, fid);

3)读取/设置类中的Int型属性

ageID = (*env)->GetFieldID(env, cls, "age", "I");

if (ageID == NULL) {

ret = -1;

printf("can not get field age");

goto destroy;

}

(*env)->SetIntField(env, jobj, ageID, 10);//int类型数据成员设置

获取int数据成员直接把SetIntField中的Set改为Get即可

jint value = env->GetIntField(env, obj, fid);

6.使用结束后

destroy:

(*jvm)->DestroyJavaVM(jvm);

return ret;

Java之JNI的介绍与应用20170622的更多相关文章

  1. Android JNI技术介绍【转】

    本文转载自:http://blog.csdn.net/yangwen123/article/details/8085833 JNI是JavaNative Interface 的缩写,通过JNI,Jav ...

  2. Java筑基 - JNI到底是个啥

    在前面介绍Unsafe的文章中,简单的提到了java中的本地方法(Native Method),它可以通过JNI(Java Native Interface)调用其他语言中的函数来实现一些相对底层的功 ...

  3. Java 16 新功能介绍

    点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 程序猿阿朗博客 已经收录,有很多知识点和系列文章. Ja ...

  4. Java 17 新功能介绍(LTS)

    点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. Jav ...

  5. Java 19 新功能介绍

    点赞再看,动力无限. 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. Java 19 在2022 年 9 ...

  6. JAVA使用JNI调用C++动态链接库

    JAVA使用JNI调用C++动态链接库 使用JNI连接DLL动态链接库,并调用其中的函数 首先 C++中写好相关函数,文件名为test.cpp,使用g++编译为DLL文件,指令如下: g++ -sha ...

  7. 【Java的JNI快速学习教程】

    1. JNI简介 JNI是Java Native Interface的英文缩写,意为Java本地接口. 问题来源:由于Java编写底层的应用较难实现,在一些实时性要求非常高的部分Java较难胜任(实时 ...

  8. java通过jni方式获取硬盘序列号(windows,linux)

    linux系统java通过jni方式获取硬盘序列号 http://blog.csdn.net/starter110/article/details/8186788 使用jni在windows下读取硬盘 ...

  9. JAVA中JNI的简单使用

    了解JNI:JAVA因其跨平台特性而受人们喜爱,也正因此,使得它和本机各种内部联系变得很少,所以JNI(Java Native Interface)就是用来解决JAVA本地操作的一种方式.JAVA通过 ...

随机推荐

  1. 用Python实现多站点运维监控

    在小型公司里如果产品线单一的话,比如就一个app, 一般1~2个运维就够用了.如果产品过于庞大,就需要多个运维人员. 但对于多产品线的公司来说,运维人员就要必须分多个人负责,因为超过200个站点让1个 ...

  2. 【java请求】- jmeter_jdbc脚本实战

    一,导入 使用Jmeter运行Java脚本,需要用到Jmeter的提供的框架jar包(分别在jmeter目录下的lib和ext目录下)1.ApacheJMeter_core.jar2.ApacheJM ...

  3. Mybatis中的几种注解映射

    1.  普通映射 2. @Select("select * from mybatis_Student where id=#{id}") 3. public Student getS ...

  4. Ryu学习总结(持续更新)

    Ryu学习总结 该篇学习笔记,与其他分析Ryu控制器代码的笔记不同,主要按照程序的构成来进行分块总结,由于本人为新手入门,不能保证没有错误,如果发现错误,欢迎指教. 以下的内容主要来源: 源码 官方文 ...

  5. Jenkins 自动化测试

    学习 Jenkins 自动化测试的系列文章 Robot Framework 概念 Robot Framework 安装 Pycharm + Robot Framework 环境搭建 Robot Fra ...

  6. Numpy入门笔记第三天

    __TITLE__ = "利用Numpy进行历史股价分析" __DATASOURCE__ = "ATAGURU" # CSV文件读取 import numpy ...

  7. 论文笔记:Visual Object Tracking based on Adaptive Siamese and Motion Estimation Network

    Visual Object Tracking based on Adaptive Siamese and Motion Estimation 本文提出一种利用上一帧目标位置坐标,在本帧中找出目标可能出 ...

  8. UUID.randomUUID()简单介绍

    UUID含义是通用唯一识别码 (Universally Unique Identifier),这 是一个软件建构的标准,也是被开源软件基金会 (Open Software Foundation, OS ...

  9. c# 导入第三方插件(例如pdf控件),莫名有时候成功有时候出错

    问题情境: 正如标题所述: 解决办法: 怀疑是adobe acrobat 9 pro安装文件出错:重新安装,成功. 在这过程中,尝试过福听阅读器,adobe reader等,均正常. 注:1.第三方的 ...

  10. myeclipse和ecplise中安装git插件的问题

    我的myeclipse10.7和ecplise helis一直安装不了git插件,myeclipse中details说我的myeclipse少了team_features等之类文件,helis的情况大 ...