java JNI介绍

JNI是Java Native Interface的全称。

oracle文档中是这样描述的

The JNI is a native programming interface. It allows Java code that runs inside a Java Virtual Machine (VM) to interoperate with applications and libraries written in other programming languages, such as C, C++, and assembly.

翻译过来就是 JNI 是本机编程接口。 它允许在 Java 虚拟机 (VM) 内运行的 Java 代码与使用其他编程语言(例如 C、C++ 和汇编)编写的应用程序和库进行互操作。

说白了就是java代码和其他编程语言的代码相互调用。

今天就主要记录下日常的一些使用方式

1、 Java调用C++代码

  1. 首先创建我们的java类 JNIDemo.java

    package com.wbo112.jni;
    
    import java.util.*;
    import java.util.concurrent.*; public class JNIDemo {
    public native String callHello(); public native boolean sendMsg(String str); //保存JNI返回的结果
    private BlockingQueue<Entry> queue = new ArrayBlockingQueue<Entry>(10); //处理jni返回的任务 线程池
    private ExecutorService executor = Executors.newSingleThreadExecutor(); public static void main(String[] args) throws InterruptedException { //加载so文件 libJNIDemo.so
    System.loadLibrary("JNIDemo"); //System.load(); 也可以用这种方式,参数是so文件的全路径
    JNIDemo jniDemo = new JNIDemo(); //保存需要提交的任务
    Set<String> sets = new ConcurrentSkipListSet<>(); String str;
    for (int i = 0; i < 10; i++) {
    str = UUID.randomUUID().toString();
    sets.add(str);
    System.out.println("commit task :" + str); //在这里会调用jni 提交任务,jni中会新启动一个线程,异步去执行任务,执行完了会调用putEntry方法,添加到需要回调的任务列表中
    jniDemo.sendStr(str);
    } Thread thread = new Thread(() -> {
    while (true) { try {
    Entry entry = jniDemo.queue.take();
    sets.remove(entry.str); //在线程中中,进行回调通知,比如向调用方发送任务处理结果
    jniDemo.executor.execute(() -> System.out.println(entry.str + "process finish")); //所有任务进行回调后,结束添加回调任务线程,同时关闭线程池
    if (sets.isEmpty()) {
    jniDemo.executor.shutdown();
    break;
    }
    } catch (InterruptedException e) {
    System.out.println("thread interrupt "); jniDemo.executor.shutdown();
    break;
    } }
    });
    thread.start(); //也可以通过这种方式,比如在其他地方在中断回调任务的添加执行,关闭线程池
    //thread.interrupt(); } public boolean sendStr(String str) {
    return sendMsg(str);
    } public void putEntry(Entry entry) {
    queue.add(entry);
    } private static class Entry { //表示一个任务
    private String str; //表示任务处理结果
    private String result; public String getStr() {
    return str;
    } public void setStr(String str) {
    this.str = str;
    } public String getResult() {
    return result;
    } public void setResult(String result) {
    this.result = result;
    } @Override
    public String toString() {
    return "Entry{" +
    "str='" + str + '\'' +
    ", result='" + result + '\'' +
    '}';
    }
    } }
  2. 编译成class文件

    javac com/wbo112/jni/JNIDemo.java
  3. 生成头文件

    javah com.wbo112.jni.JNIDemo

    这时就会在当前目录下生成com_wbo112_jni_JNIDemo.h。

  4. 编写对应的cpp文件 JNIDemo1.cpp

    #include "com_wbo112_jni_JNIDemo.h"
    #include <iostream>
    #include <thread>
    #include <cstdlib>
    #include <ctime>
    #include <unistd.h> JavaVM *vm=NULL; unsigned seed; //在java加载so文件的时候,就会调用到这个JNI_OnLoad
    //oracle文档这里有介绍:https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#JNJI_OnLoad
    jint JNI_OnLoad(JavaVM *jvm, void *reserved){
    //这个表示java虚拟机,这个需要保存下来,因为JNIEnv只是当前线程有效,如果要在其他线程获取JNIEnv,就需要通过这个jvm来获取,后面有相应代码
    vm=jvm; //这个是为了模拟后面的回调时间,业务中应该不关注
    seed = time(0);
    srand(seed); return JNI_VERSION_1_8; } //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#JNI_OnUnload
    //so文件卸载的时候,会执行这个函数,在这里面可以做一些清理工作
    void JNI_OnUnload_L(JavaVM *vm, void *reserved){
    vm=NULL;
    } void threadfunc(jobject jobj,jstring jstr)
    {
    JNIEnv* env = NULL;
    sleep(rand() % 10); //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#GetEnv
    //这里有介绍,如果不是当前线程,是获取不到env的,这时,返回值就是JNI_EDETACHED,这时就需要调用AttachCurrentThread,给当前线程绑定env
    jint status = vm->GetEnv((void **)&env, JNI_VERSION_1_8); if (status == JNI_EDETACHED) {
    //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#AttachCurrentThread
    if (vm->AttachCurrentThread((void **)&env, NULL)!= JNI_OK){
    return;
    } } else if(status!= JNI_OK){
    std::cout<<"getEnv err"<<std::endl;
    return; } //这个是将jstring转成char*
    const char *str = env->GetStringUTFChars(jstr, 0); std::cout << "start process task " + (std::string)str << std::endl; //通过对象获取对应的类
    //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetObjectClass
    jclass jcls=env->GetObjectClass(jobj); //另一种方式获取对应的类
    //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#FindClass
    jclass jentrycls=env->FindClass("com/wbo112/jni/JNIDemo$Entry"); //获取无参的构造方法
    //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetMethodID
    jmethodID jinit=env->GetMethodID(jcls,"<init>", "()V"); //调用构造方法获取对象
    //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#NewObject
    jobject jentryobj=env->NewObject(jentrycls,jinit); //获取entry类的str字段
    //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetFieldID
    jfieldID jfieldStr=env->GetFieldID(jentrycls,"str","Ljava/lang/String;"); //给str字段设置值
    //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#Set_type_Field_routines
    env->SetObjectField(jentryobj,jfieldStr,jstr);
    char* msg = "Hello World!";
    jstring result = env->NewStringUTF(msg); jfieldID jfieldResult=env->GetFieldID(jentrycls,"result","Ljava/lang/String;"); env->SetObjectField(jentryobj,jfieldResult,result); //获取方法
    //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetMethodID
    jmethodID jmtd=env->GetMethodID(jcls,"putEntry", "(Lcom/wbo112/jni/JNIDemo$Entry;)V"); //调用方法
    //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#Call_type_Method_routines
    env->CallVoidMethod(jobj,jmtd,jentryobj);
    std::cout<<" end process task " + (std::string)str<<std::endl;
    //释放前面构造的char*
    env->ReleaseStringUTFChars( jstr, str); //释放全局对象
    //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#DeleteGlobalRef
    env->DeleteGlobalRef(jobj);
    env->DeleteGlobalRef(jstr); //当前线程和jvm进行分离
    //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#DetachCurrentThread
    vm->DetachCurrentThread();
    } JNIEXPORT jstring JNICALL Java_com_wbo112_jni_JNIDemo_callHello
    (JNIEnv *env, jobject jobj){
    char* msg = "Hello World!";
    jstring result = env->NewStringUTF(msg); // C style string to Java String
    return result; }
    JNIEXPORT jboolean JNICALL Java_com_wbo112_jni_JNIDemo_sendMsg
    (JNIEnv *env, jobject jobj, jstring jstr){ //jobject对象是不能跨线程传递的,需要先转成全局引用
    //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#NewGlobalRef
    jobject globalJobj=env->NewGlobalRef(jobj);
    jobject globalJstr=env->NewGlobalRef(jstr); //这里使用的是std::thread ,所以编译的时候需要加参数-std=c++11
    std::thread t1(threadfunc,globalJobj,(jstring)globalJstr);
    //t1.join();
    t1.detach();
    return 1; }

    编译cpp文件

    g++ -shared -std=c++11  -I $JAVA_HOME/include/linux -I $JAVA_HOME/include  -fPIC -o libJNIDemo.so JNIDemo1.cpp
  5. 执行java程序

    java -Djava.library.path=./ com.wbo112.jni.JNIDemo

2、C++代码调用java代码

这个完全就是Oracle官方的例子了

The Invocation API (oracle.com)

  1. 首先创建个ctj.cpp文件

     #include <jni.h>       /* where everything is defined */
    int main(){ JavaVM *jvm; /* denotes a Java VM */
    JNIEnv *env; /* pointer to native method interface */
    JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
    JavaVMOption* options = new JavaVMOption[1];
    options[0].optionString = "-Djava.class.path=./";
    vm_args.version = JNI_VERSION_1_8;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;
    /* load and initialize a Java VM, return a JNI interface
    * * pointer in env */
    JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    delete options;
    /* invoke the Main.test method using the JNI */
    jclass cls = env->FindClass("Main");
    jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
    env->CallStaticVoidMethod(cls, mid, 100);
    /* We are done. */
    jvm->DestroyJavaVM();
    return 0;
    }
  2. 再创建c++需要调用的java文件 Main.java

    public class Main {
    public static void test(int a) {
    System.out.println(" Main test:" + a);
    }
    }
  3. 编译ctj.cpp文件

    g++ -I $JAVA_HOME/include/linux -I $JAVA_HOME/include   -L"$JAVA_HOME/jre/lib/amd64/server/" ctj.cpp -o  ctj   -ljvm
  4. 执行生成的ctj文件

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/amd64/server/
    ./ctj

java JNI介绍的更多相关文章

  1. JAVA JNI

    jni非常好的一篇文章 http://m.blog.csdn.net/article/details?id=22827307 JAVA JNI介绍 http://blog.csdn.net/cyg08 ...

  2. 三、Android NDK编程预备之Java jni入门创建C/C++共享库

    转自: http://www.eoeandroid.com/thread-264971-1-1.html 应网友回复,答应在两天前要出一篇创建C/C++共享库的,但由于清明节假期,跟朋友出去游玩,丢手 ...

  3. 二、Android NDK编程预备之Java jni入门Hello World

    转自:  http://www.eoeandroid.com/forum.php?mod=viewthread&tid=264543&fromuid=588695 昨天已经简要介绍了J ...

  4. 一、Android NDK编程预备之Java jni简介

    转自:  http://www.eoeandroid.com/thread-264384-1-1.html 游戏开发 视频教程 博客 淘帖     论坛›eoe·Android应用开发区›Androi ...

  5. JNI介绍(转)

    源:JNI介绍 JNI是在学习Android HAL时必须要面临一个知识点,如果你不了解它的机制,不了解它的使用方式,你会被本地代码绕的晕头转向,JNI作为一个中间语言的翻译官在运行Java代码的An ...

  6. java + jni + mingw实例开发(基于命令行窗口模式)

    java+ jni + mingw 参考网址: http://wenku.baidu.com/link?url=9aQ88d2ieO7IgKLlNhJi5d3mb3xwzbezLPzSIX3ixz4_ ...

  7. Android下HelloWorld项目的R.java文件介绍

    R.java文件介绍 HelloWorld工程中的R.java文件 package com.android.hellworld; public final class R {     public s ...

  8. java JNI 调试出现的错误

    java JNI 调试出现的错误 ERROR: JDWP Unable to get JNI 1.2 environment, jvm->GetEnv() return code = -2JDW ...

  9. 深入Java虚拟机读书笔记第一章Java体系结构介绍

    第1章 Java体系结构介绍 Java技术核心:Java虚拟机 Java:安全(先天防bug的设计.内存).健壮.平台无关.网络无关(底层结构上,对象序列化和RMI为分布式系统中各个部分共享对象提供了 ...

随机推荐

  1. Swoole实现毫秒级定时任务

    项目开发中,如果有定时任务的业务要求,我们会使用linux的crontab来解决,但是它的最小粒度是分钟级别,如果要求粒度是秒级别的,甚至毫秒级别的,crontab就无法满足,值得庆幸的是swoole ...

  2. 利用 FilesystemIterator 获取指定目录下的所有文件

    /** * 获取指定目录下的所有文件 * @param null $path * @return array */ public function getFileByPath($path = null ...

  3. 你知道购买车票的原理吗?Java 线程同步

    先看再点赞,给自己一点思考的时间,如果对自己有帮助,微信搜索[程序职场]关注这个执着的职场程序员.我有什么:职场规划指导,技能提升方法,讲不完的职场故事,个人成长经验. 大周末的还是6点起床,起床的第 ...

  4. kong插件Prometheus+grafana图形化展示

    目录 1. 准备工作 3. 为kong添加 prometheus插件 4. 打开kong的metrics支持 4. 配置prometheus.yml添加kong提供的数据信息 5. 在 Grafana ...

  5. varnish4.X安装

    目录 1. rpm方式 2. 编译安装 2.1 依赖包 2.2 编译Varnish 本文提供了两种安装方式,但建议使用编译安装. 官方链接:https://varnish-cache.org/ 部署文 ...

  6. 北京大公司:你是熟悉Map集合吗?

    <对线面试官>系列目前已经连载30篇啦,这是一个讲人话面试系列 [对线面试官]Java注解 [对线面试官]Java泛型 [对线面试官] Java NIO [对线面试官]Java反射 &am ...

  7. 记录Jackson和Lombok的坑

    记录Jackson和Lombok的坑 今天遇到Jackson反序列化json缺少了字段,后来研究下发现是Jackson的机制和Lombok生成的setter不一致,导致没有正确调用setter. 复现 ...

  8. 【剑指offer】50.数组中重复出现的数字

    50.数组中重复出现的数字 知识点:数组:Set的不可重复性 题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重 ...

  9. 前端开发入门到进阶第五集【安装SublimeServer】

    参考:https://www.cnblogs.com/jf-67/p/8031614.html 1.我们可以直接在sublime text里面安装,Ctrl+shift+p进入命令模式,输入insta ...

  10. 简单快速安装Apache+PHP+MySql服务环境(一)

    由于自己只是普通的coder,对于服务器的操作不是很熟悉,在网上找了很多关于PHP和apache服务器环境搭建的帖子,不过都不尽相同,尤其是编译安装更是看的云里雾里的,所以选择了一种比较简单的方式进行 ...