一. 前言
最近接到一個項目,由於我們公司做水控設備項目,買的挺好的,有一個客戶需要做對需要我們設備接入他們的系統,於是需要我們提供二次開發接口,而且是Java 平臺的,通過由於這個項目我編寫的客戶端是使用C# 編寫的,並且都是所有接口都是集成到客戶端中,因此沒有預留給其他平臺調用的接口。由於Java Web 不能直接操作硬件。因此,需要我提供Java 能調用的接口,Java能調用的接口並且可以直接操作硬件,只能是C/C++ 。於是,通過JNI Java可以間接操作我們公司的USB設備,於是寫下這篇文件,記錄JNI 編寫的過程,防止下次忘記了,又需要重新學習。
二. 簡介
2.1 什麼是JNI
JNI(Java Native Interface)意爲JAVA本地調用,它允許Java代碼和其他語言寫的代碼進行交互相(一般爲C/C++),簡單的說,一種在Java虛擬機控制下執行代碼的標準機制。
2.2 JNIEnv與JavaVM
JNIEnv 概念 : 是一個線程相關的結構體, 該結構體代表了 Java 在本線程的運行環境 ;
JNIEnv 與 JavaVM : 注意區分這兩個概念;
-- JavaVM : JavaVM 是 Java虛擬機在 JNI 層的代表, JNI 全局只有一個;
-- JNIEnv : JavaVM 在線程中的代表, 每個線程都有一個, JNI 中可能有很多個 JNIEnv;
JNIEnv 作用 :
-- 調用 Java 函數 : JNIEnv 代表 Java 運行環境, 可以使用 JNIEnv 調用 Java 中的代碼;
-- 操作 Java 對象 : Java 對象傳入 JNI 層就是 Jobject 對象, 需要使用 JNIEnv 來操作這個 Java 對象;
摘自《JNI 實戰全面解析》
2.3
詳細關於JNI 相關可以查看大神文章,講的非常好《JNI 實戰全面解析》
三. 開發環境
(1) JDK v1.8 (2) Eclipse 2018-12 (4.10.0) (3) Visual Studio 2015
四. JNI 相關代碼編寫
4.1 編寫由Java native 類接口,通過javah 命令生成.h文件
java 類
package com.chen.jni;
public class JNIInterface
{
//由java 調用c/c++ 接口打印信息
public native void hellojni();
//java 傳遞 byte 數據到C/C++ 中,並且修改
public native void testByteArray(byte[] stream);
static {
System.loadLibrary("testjni");
}
}
使用找到JNIInterface.java 文件,使用在Windows 控制檯中javah -classpath . -jni com.chen.JNIInterface 生成對應.h 文件
如下圖:
4.2 使用VS 創建DLL 工程,編寫JNI 到x64動態庫
4.2.1 新建Win32 控制檯工程
4.2.2 選擇DLL
4.2.3 將使用javah 生成的.h 文件拷貝到Win32 DLL 工程目錄下
4.2.4 將.h 文件添加到工程上和jni.h 相關的文件也拷貝到工程目錄下,並且添加到工程中將jni.h 和相關文件拷貝出來,是方便當工程移動到別的電腦照樣可以直接編譯。
4.2.5 編寫同名.cpp 文件,並且實現接口
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "com_chen_jni_JNIInterface.h"
/*
* Class: com_chen_jni_JNIInterface
* Method: hellojni
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_chen_jni_JNIInterface_hellojni
(JNIEnv *env, jobject jobj)
{
printf("helllo jni");
}
/*
* Class: com_chen_jni_JNIInterface
* Method: testByteArray
* Signature: ([B)V
*/
JNIEXPORT void JNICALL Java_com_chen_jni_JNIInterface_testByteArray
(JNIEnv * env, jobject jobj, jbyteArray jByteArray)
{
int ik = 0;
unsigned char data[4] = { 0 };
env->GetByteArrayRegion(jByteArray, 0, 4, (jbyte *)data);
printf("java to c++ data\n");
for (ik = 0; ik < 4; ik++)
{
printf("data[%d] = %.2X ", ik, data[ik]);
data[ik] = data[ik] + 1;
}
env->SetByteArrayRegion(jByteArray, 0, 4, (jbyte *)data);
}
4.2.6 在C/C++ 工程配置中,添加JNI 目錄依賴
4.2.7 根據使用場景,配置工程編譯爲64位DLL 或者32 位DLL.
4.3 在Eclipse 中Java 通過JNI 調用C++
4.3.1 將生成的DLL 拷貝到java 工程目錄下
4.3.2 配置工程,增加JNI 庫支持
然後,就可以調用了。