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

    1. package com.wbo112.jni;
    2. import java.util.*;
    3. import java.util.concurrent.*;
    4. public class JNIDemo {
    5. public native String callHello();
    6. public native boolean sendMsg(String str);
    7. //保存JNI返回的结果
    8. private BlockingQueue<Entry> queue = new ArrayBlockingQueue<Entry>(10);
    9. //处理jni返回的任务 线程池
    10. private ExecutorService executor = Executors.newSingleThreadExecutor();
    11. public static void main(String[] args) throws InterruptedException {
    12. //加载so文件 libJNIDemo.so
    13. System.loadLibrary("JNIDemo");
    14. //System.load(); 也可以用这种方式,参数是so文件的全路径
    15. JNIDemo jniDemo = new JNIDemo();
    16. //保存需要提交的任务
    17. Set<String> sets = new ConcurrentSkipListSet<>();
    18. String str;
    19. for (int i = 0; i < 10; i++) {
    20. str = UUID.randomUUID().toString();
    21. sets.add(str);
    22. System.out.println("commit task :" + str);
    23. //在这里会调用jni 提交任务,jni中会新启动一个线程,异步去执行任务,执行完了会调用putEntry方法,添加到需要回调的任务列表中
    24. jniDemo.sendStr(str);
    25. }
    26. Thread thread = new Thread(() -> {
    27. while (true) {
    28. try {
    29. Entry entry = jniDemo.queue.take();
    30. sets.remove(entry.str);
    31. //在线程中中,进行回调通知,比如向调用方发送任务处理结果
    32. jniDemo.executor.execute(() -> System.out.println(entry.str + "process finish"));
    33. //所有任务进行回调后,结束添加回调任务线程,同时关闭线程池
    34. if (sets.isEmpty()) {
    35. jniDemo.executor.shutdown();
    36. break;
    37. }
    38. } catch (InterruptedException e) {
    39. System.out.println("thread interrupt ");
    40. jniDemo.executor.shutdown();
    41. break;
    42. }
    43. }
    44. });
    45. thread.start();
    46. //也可以通过这种方式,比如在其他地方在中断回调任务的添加执行,关闭线程池
    47. //thread.interrupt();
    48. }
    49. public boolean sendStr(String str) {
    50. return sendMsg(str);
    51. }
    52. public void putEntry(Entry entry) {
    53. queue.add(entry);
    54. }
    55. private static class Entry {
    56. //表示一个任务
    57. private String str;
    58. //表示任务处理结果
    59. private String result;
    60. public String getStr() {
    61. return str;
    62. }
    63. public void setStr(String str) {
    64. this.str = str;
    65. }
    66. public String getResult() {
    67. return result;
    68. }
    69. public void setResult(String result) {
    70. this.result = result;
    71. }
    72. @Override
    73. public String toString() {
    74. return "Entry{" +
    75. "str='" + str + '\'' +
    76. ", result='" + result + '\'' +
    77. '}';
    78. }
    79. }
    80. }
  2. 编译成class文件

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

    1. javah com.wbo112.jni.JNIDemo

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

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

    1. #include "com_wbo112_jni_JNIDemo.h"
    2. #include <iostream>
    3. #include <thread>
    4. #include <cstdlib>
    5. #include <ctime>
    6. #include <unistd.h>
    7. JavaVM *vm=NULL;
    8. unsigned seed;
    9. //在java加载so文件的时候,就会调用到这个JNI_OnLoad
    10. //oracle文档这里有介绍:https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#JNJI_OnLoad
    11. jint JNI_OnLoad(JavaVM *jvm, void *reserved){
    12. //这个表示java虚拟机,这个需要保存下来,因为JNIEnv只是当前线程有效,如果要在其他线程获取JNIEnv,就需要通过这个jvm来获取,后面有相应代码
    13. vm=jvm;
    14. //这个是为了模拟后面的回调时间,业务中应该不关注
    15. seed = time(0);
    16. srand(seed);
    17. return JNI_VERSION_1_8;
    18. }
    19. //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#JNI_OnUnload
    20. //so文件卸载的时候,会执行这个函数,在这里面可以做一些清理工作
    21. void JNI_OnUnload_L(JavaVM *vm, void *reserved){
    22. vm=NULL;
    23. }
    24. void threadfunc(jobject jobj,jstring jstr)
    25. {
    26. JNIEnv* env = NULL;
    27. sleep(rand() % 10);
    28. //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#GetEnv
    29. //这里有介绍,如果不是当前线程,是获取不到env的,这时,返回值就是JNI_EDETACHED,这时就需要调用AttachCurrentThread,给当前线程绑定env
    30. jint status = vm->GetEnv((void **)&env, JNI_VERSION_1_8);
    31. if (status == JNI_EDETACHED) {
    32. //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#AttachCurrentThread
    33. if (vm->AttachCurrentThread((void **)&env, NULL)!= JNI_OK){
    34. return;
    35. }
    36. } else if(status!= JNI_OK){
    37. std::cout<<"getEnv err"<<std::endl;
    38. return;
    39. }
    40. //这个是将jstring转成char*
    41. const char *str = env->GetStringUTFChars(jstr, 0);
    42. std::cout << "start process task " + (std::string)str << std::endl;
    43. //通过对象获取对应的类
    44. //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetObjectClass
    45. jclass jcls=env->GetObjectClass(jobj);
    46. //另一种方式获取对应的类
    47. //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#FindClass
    48. jclass jentrycls=env->FindClass("com/wbo112/jni/JNIDemo$Entry");
    49. //获取无参的构造方法
    50. //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetMethodID
    51. jmethodID jinit=env->GetMethodID(jcls,"<init>", "()V");
    52. //调用构造方法获取对象
    53. //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#NewObject
    54. jobject jentryobj=env->NewObject(jentrycls,jinit);
    55. //获取entry类的str字段
    56. //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetFieldID
    57. jfieldID jfieldStr=env->GetFieldID(jentrycls,"str","Ljava/lang/String;");
    58. //给str字段设置值
    59. //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#Set_type_Field_routines
    60. env->SetObjectField(jentryobj,jfieldStr,jstr);
    61. char* msg = "Hello World!";
    62. jstring result = env->NewStringUTF(msg);
    63. jfieldID jfieldResult=env->GetFieldID(jentrycls,"result","Ljava/lang/String;");
    64. env->SetObjectField(jentryobj,jfieldResult,result);
    65. //获取方法
    66. //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetMethodID
    67. jmethodID jmtd=env->GetMethodID(jcls,"putEntry", "(Lcom/wbo112/jni/JNIDemo$Entry;)V");
    68. //调用方法
    69. //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#Call_type_Method_routines
    70. env->CallVoidMethod(jobj,jmtd,jentryobj);
    71. std::cout<<" end process task " + (std::string)str<<std::endl;
    72. //释放前面构造的char*
    73. env->ReleaseStringUTFChars( jstr, str);
    74. //释放全局对象
    75. //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#DeleteGlobalRef
    76. env->DeleteGlobalRef(jobj);
    77. env->DeleteGlobalRef(jstr);
    78. //当前线程和jvm进行分离
    79. //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#DetachCurrentThread
    80. vm->DetachCurrentThread();
    81. }
    82. JNIEXPORT jstring JNICALL Java_com_wbo112_jni_JNIDemo_callHello
    83. (JNIEnv *env, jobject jobj){
    84. char* msg = "Hello World!";
    85. jstring result = env->NewStringUTF(msg); // C style string to Java String
    86. return result;
    87. }
    88. JNIEXPORT jboolean JNICALL Java_com_wbo112_jni_JNIDemo_sendMsg
    89. (JNIEnv *env, jobject jobj, jstring jstr){
    90. //jobject对象是不能跨线程传递的,需要先转成全局引用
    91. //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#NewGlobalRef
    92. jobject globalJobj=env->NewGlobalRef(jobj);
    93. jobject globalJstr=env->NewGlobalRef(jstr);
    94. //这里使用的是std::thread ,所以编译的时候需要加参数-std=c++11
    95. std::thread t1(threadfunc,globalJobj,(jstring)globalJstr);
    96. //t1.join();
    97. t1.detach();
    98. return 1;
    99. }

    编译cpp文件

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

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

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

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

The Invocation API (oracle.com)

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

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

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

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

    1. export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/amd64/server/
    2. ./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. leetcode1047

    思路分析,这题是在栈分类的题目的,所以顺便复习下数据结构,先用java现成的,就当复习了. 1.判断栈顶和插入的元素是否相等,如果相等,那就出栈,不相等入栈结束 java版:

  2. [小技巧] gcc 编译选项-###

    原文译至:http://elinux.org/GCC_Tips 的一小部分. -###编译选项用于查看编译的过程 gcc -### <你的命令行的其他部分放在这里> 你运行的GCC其是一系 ...

  3. buu 刮开有奖

    一.查壳, 二.拖入ida,分析 直接搜字符串完全没头绪,在看了大佬的wp才找到了,关键函数. 明显那个String就是我们要求的flag,要开始分析程序. 字符串长度为8,同时这个函数对字符串进行了 ...

  4. HCNA Routing&Switching之动态路由基本概念

    前文我们了解了静态路由的相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/14965433.html:今天我们来聊一聊动态路由相关概念: 首先我们要清楚什 ...

  5. CA和SSL证书介绍

    一.什么是CA? CA是证书的签发机构,它是公钥基础设施(Public Key Infrastructure,PKI)的核心.CA是负责签发证书.认证证书.管理已颁发证书的机关.CA 拥有一个证书(内 ...

  6. IP地址详解

    讲之前了解一些网络设备的作用: 交换机:组建局域网 路由器:连接内外网 网关:一个网络的出口(Gate Way = GW)一般网关在路由器上 局域网(也称内网) 一个简单的局域网的基本组成设备:交换机 ...

  7. Django基础-02篇 Models的属性与字段

    1.models字段类型 AutoField():一个IntegerField,根据可用ID自动递增.如果没指定主键,就创建它自动设置为主键. IntegerField():一个整数: FloatFi ...

  8. CentOS 命令提示符

    命令提示符的设置就是对PS1的配置: export PS1="\[\e[35;40m\][\[\e[32;40m\]\u\[\e[37;40m\]@\[\e[36;40m\]\h  \[\e ...

  9. 【16位RAW图像处理三】直方图均衡化及局部直方图均衡用于16位图像的细节增强。

    通常我们生活中遇到的图像,无论是jpg.还是png或者bmp格式,一般都是8位的(每个通道的像素值范围是0-255),但是随着一些硬件的发展,在很多行业比如医疗.红外.航拍等一些场景下,拥有更宽的量化 ...

  10. 【剑指offer】05. 替换空格

    剑指 Offer 05. 替换空格 知识点:: 题目描述 请实现一个函数,把字符串 s 中的每个空格替换成"%20". 示例 输入:s = "We are happy.& ...