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

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. Vue学习计划基础笔记(四) - 事件处理

    事件处理 目标: 熟练掌握事件监听的方式,熟悉事件处理方式以及各类事件修饰符 理解在html中监听事件的意义 监听事件(v-on) 类似普通的on,例如v-on:click或@click就相当于普通的 ...

  2. 【洛谷】题解 P1056 【排座椅】

    题目链接 因为题目说输入保证会交头接耳的同学前后相邻或者左右相邻,所以一对同学要分开有且只有一条唯一的通道才能把他们分开. 于是可以吧这条通道累加到一个数组里面.应为题目要求纵列的通道和横列的通道条数 ...

  3. JAVA基础学习之路(十二)链表

    定义链表的基本结构: class Link {//外部类 //内部类,只为链表类服务 private class Node {//定义节点类 private String data;//保存的数据 p ...

  4. Numpy入门笔记第三天

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

  5. LeetCode 148——排序链表

    1. 题目 2. 解答 2.1 快速排序 可参考 快速排序和归并排序 中的第一种快速排序思想,与在数组中排序有两点不同. 第一,我们需要取最后一个元素作为主元,在数组中可以直接访问到最后一个元素,但在 ...

  6. 性能测试持续集成(Jenkins+Ant+Jmeter)

    一.环境准备: 1.JDK:http://www.oracle.com/technetwork/java/javase/downloads/index.html 2.Jmeter:http://jme ...

  7. AppCan 之初体验

    平台概述 什么是AppCan 移步这里,楼主的一句话:可以匹敌 Phonegap .Titanium .Sencha Touch .MUI .ImagApp.Nitrous .apicloud .起步 ...

  8. Beta发布——视频博客

    1.视频链接 视频上传至优酷自频道,地址链接:http://v.youku.com/v_show/id_XMzkzNzAxNDk2OA==.html?spm=a2hzp.8244740.0.0 2.视 ...

  9. 作业 20181030-3互评Alpha版本

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2323 组名:可以低头,但没必要 组长:付佳 组员:张俊余  李文涛  孙 ...

  10. 【IdentityServer4文档】- 使用客户端凭据保护 API

    使用客户端凭据保护 API quickstart 介绍了使用 IdentityServer 保护 API 的最基本场景. 接下来的场景,我们将定义一个 API 和一个想要访问它的客户端. 客户端将在 ...