java JNI介绍
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++代码
首先创建我们的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 + '\'' +
'}';
}
} }编译成class文件
javac com/wbo112/jni/JNIDemo.java
生成头文件
javah com.wbo112.jni.JNIDemo
这时就会在当前目录下生成com_wbo112_jni_JNIDemo.h。
编写对应的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
执行java程序
java -Djava.library.path=./ com.wbo112.jni.JNIDemo
2、C++代码调用java代码
这个完全就是Oracle官方的例子了
首先创建个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;
}再创建c++需要调用的java文件 Main.java
public class Main {
public static void test(int a) {
System.out.println(" Main test:" + a);
}
}编译ctj.cpp文件
g++ -I $JAVA_HOME/include/linux -I $JAVA_HOME/include -L"$JAVA_HOME/jre/lib/amd64/server/" ctj.cpp -o ctj -ljvm
执行生成的ctj文件
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/amd64/server/
./ctj
java JNI介绍的更多相关文章
- JAVA JNI
jni非常好的一篇文章 http://m.blog.csdn.net/article/details?id=22827307 JAVA JNI介绍 http://blog.csdn.net/cyg08 ...
- 三、Android NDK编程预备之Java jni入门创建C/C++共享库
转自: http://www.eoeandroid.com/thread-264971-1-1.html 应网友回复,答应在两天前要出一篇创建C/C++共享库的,但由于清明节假期,跟朋友出去游玩,丢手 ...
- 二、Android NDK编程预备之Java jni入门Hello World
转自: http://www.eoeandroid.com/forum.php?mod=viewthread&tid=264543&fromuid=588695 昨天已经简要介绍了J ...
- 一、Android NDK编程预备之Java jni简介
转自: http://www.eoeandroid.com/thread-264384-1-1.html 游戏开发 视频教程 博客 淘帖 论坛›eoe·Android应用开发区›Androi ...
- JNI介绍(转)
源:JNI介绍 JNI是在学习Android HAL时必须要面临一个知识点,如果你不了解它的机制,不了解它的使用方式,你会被本地代码绕的晕头转向,JNI作为一个中间语言的翻译官在运行Java代码的An ...
- java + jni + mingw实例开发(基于命令行窗口模式)
java+ jni + mingw 参考网址: http://wenku.baidu.com/link?url=9aQ88d2ieO7IgKLlNhJi5d3mb3xwzbezLPzSIX3ixz4_ ...
- Android下HelloWorld项目的R.java文件介绍
R.java文件介绍 HelloWorld工程中的R.java文件 package com.android.hellworld; public final class R { public s ...
- java JNI 调试出现的错误
java JNI 调试出现的错误 ERROR: JDWP Unable to get JNI 1.2 environment, jvm->GetEnv() return code = -2JDW ...
- 深入Java虚拟机读书笔记第一章Java体系结构介绍
第1章 Java体系结构介绍 Java技术核心:Java虚拟机 Java:安全(先天防bug的设计.内存).健壮.平台无关.网络无关(底层结构上,对象序列化和RMI为分布式系统中各个部分共享对象提供了 ...
随机推荐
- js 简单实现获取短信按钮倒计时60秒
<!DOCTYPE html><html lang="en"><head> <meta http-equiv="Content- ...
- WPF教程五:附加依赖项属性
附加依赖项属性是一个属性本来不属于对象自己,但是某些特定场景其他的对象要使用该对象在这种场景下的值.这个值只在这个场景下使用.基于这个需求设计出来的属性.这里主要涉及到一个解耦问题.最大的优势是在特定 ...
- 使用Azure Congnitive Services 技术制作AI故事机
引言 前一段时间有幸参加了微软MVP的AI方面的学习挑战赛,对于AI 这个新的领域的技术瞬间勾起了我的学习兴趣. 最近几年,不管是国内还是国外,AI都是一个异常火热的词.比如现在的自动驾驶技术,其实就 ...
- 深入理解Java并发类——AQS
目录 什么是AQS 为什么需要AQS AQS的核心思想 AQS的内部数据和方法 如何利用AQS实现同步结构 ReentrantLock对AQS的利用 尝试获取锁 获取锁失败,排队竞争 参考 什么是AQ ...
- 将gitlab内置node_exporter提供外部prometheus使用
目录 修改gitlab的配置 重新初始化配置 gitlab服务已经包含了node_exporter服务,但是配置文件限制了9100端口的访问,所以主机信息不能直接被外部的prometheus收集 修改 ...
- JAVA中自增自减运算符(i++与++i的区别)
注意: 自增运算符和自减运算符只能用于变量,而不能用于常亮或表达式 运算符 运算 范例 结果 ++ 自增(前):先运算后取值 a=2;b=++a; a=3;b=3; ++ 自增(后):先取值后运算 a ...
- urllib3使用池管理发送请求和requests常用方法的基本使用+session使用
使用urllib3的池管理器 urllib3是在urllib进行更加深入的改进,最大的好处就是在urllib的基础上添加了池管理,以至于我们不需要再去注意我们需要由那个链接去发送请求,而只需要获取到链 ...
- prignMVC+myBatis整合—— 基于MapperFactoryBean
学习本节内容请先看"MyBatis的基本应用".地址:http://lydia-fly.iteye.com/admin/blogs/2152948 Spring与MyBatis整合 ...
- grub2引导各种ISO系统镜像
1 grub2引导winpe 1下载winpe.iso镜像,放置在U盘的根目录下 2下载syslinux的memdisk,放置在U盘的根目录下 3 set root=(hd0,msdos1) 设置U ...
- Postman进行webservices接口测试
1.接口地址 webservices是什么? 更多webservices接口地址访问地址:http://www.webxml.com.cn/zh_cn/web_services.aspx webser ...