jni基本概念不在介紹,本文介紹基於springboot項目,在自己寫的動態庫中去調用其他C動態庫。
1項目結構
代碼下載:https://download.csdn.net/download/h4241778/12272194
2 java類
package com.sec.iot.jni;
public class GetCFuncJni {
/**
*
* 返回整型
* @return
*/
public static native int start4G();
/**
* 返回字符串
*
* @return
*
*/
public static native String get4GInfo();
/**
* 傳參
*/
public static native int setWiredNetworkStaticIp(String ip, String netmask,
String gw, String dns1, String dns2);
}
3編譯java文件
3.1javac GetCFunc.java 生產class文件
註釋中帶有中文,需要javac -encoding utf-8 MyFirstJavaProgram.java
3.2javah生成頭文件,在文件所在包路徑下執行命令
javah com.sec.iot.jni.GetCFuncJni
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_sec_iot_jni_GetCFuncJni */
#ifndef _Included_com_sec_iot_jni_GetCFuncJni
#define _Included_com_sec_iot_jni_GetCFuncJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_sec_iot_jni_GetCFuncJni
* Method: start4G
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_sec_iot_jni_GetCFuncJni_start4G
(JNIEnv *, jclass);
/*
* Class: com_sec_iot_jni_GetCFuncJni
* Method: get4GInfo
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_sec_iot_jni_GetCFuncJni_get4GInfo
(JNIEnv *, jclass);
/*
* Class: com_sec_iot_jni_GetCFuncJni
* Method: setWiredNetworkStaticIp
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_sec_iot_jni_GetCFuncJni_setWiredNetworkStaticIp
(JNIEnv *, jclass, jstring, jstring, jstring, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif
4編寫native :java和c的中間件
通過dlopen,dlsym,dlclose動態調用其他動態庫方法;可以多線程打開庫調用,相互獨立的。
#include "com_sec_iot_jni_GetCFuncJni.h"
#include <stdio.h>
#include <dlfcn.h>
struct MidStruct {
int result;
char json[256];
};
/*
* Class: com_sec_iot_jni_GetCFuncJni
* Method: start4G
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_sec_iot_jni_GetCFuncJni_start4G
(JNIEnv * env, jclass jc){
/*手動加載指定位置的so動態庫*/
void* handle = dlopen("libhardwareInfo2.so", RTLD_LAZY);
int (*start4G)();
/*根據動態鏈接庫操作句柄與符號,返回符號對應的地址*/
start4G = dlsym(handle, "start_4g");
int rv=start4G();
dlclose(handle);
return rv;
}
/*
* Class: com_sec_iot_jni_GetCFuncJni
* Method: get4GInfo
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_sec_iot_jni_GetCFuncJni_get4GInfo
(JNIEnv * env, jclass jc){
void* handle = dlopen("libhardwareInfo2.so", RTLD_LAZY);
struct MidStruct (*midFuncName)();
midFuncName = dlsym(handle, "get_4g_info");
struct MidStruct midres;
midres = midFuncName();
char* cj="404";
jstring rtn;
if(midres.result==0){
cj= midres.json;
}
rtn = (*env)->NewStringUTF(env, cj);
dlclose(handle);
return rtn;
}
/*
* Class: com_sec_iot_jni_GetCFuncJni
* Method: setWiredNetworkStaticIp
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_sec_iot_jni_GetCFuncJni_setWiredNetworkStaticIp
(JNIEnv * env, jclass jc, jstring ip, jstring netmask, jstring gw, jstring dns1, jstring dns2){
char *cip = (*env)->GetStringUTFChars(env, ip, 0);
char *cnetmask = (*env)->GetStringUTFChars(env, netmask, 0);
char *cgw = (*env)->GetStringUTFChars(env, gw, 0);
char *cdns1 = (*env)->GetStringUTFChars(env, dns1, 0);
char *cdns2 = (*env)->GetStringUTFChars(env, dns2, 0);
void* handle = dlopen("libhardwareInfo2.so", RTLD_LAZY);
int (*midFuncName)(char*,char*,char*,char*,char*);
midFuncName = dlsym(handle, "set_wired_net_work_static_ip");
int rv=midFuncName(cip,cnetmask,cgw,cdns1,cdns2);
(*env)->ReleaseStringUTFChars(env,ip, cip);
(*env)->ReleaseStringUTFChars(env,netmask, cnetmask);
(*env)->ReleaseStringUTFChars(env,gw, cgw);
(*env)->ReleaseStringUTFChars(env,dns1, cdns1);
(*env)->ReleaseStringUTFChars(env,dns2, cdns2);
dlclose(handle);
//(*env)->ReleaseStringUTFChars(env, cip, ip,netmask,cnetmask,gw,cgw,dns1,dns2);
return rv;
}
5生成動態庫放在native 下
5.1sudo gcc -I /opt/jdk1.8.0_241/include -I /opt/jdk1.8.0_241/include/linux -fPIC -c getcfunc.c
/opt/jdk1.8.0_241/include /opt/jdk1.8.0_241/include/linux 根據實際jre中jni.h路徑而定
5.2gcc -shared -Wl,-soname,libgetcfunc.so.1 -o libgetcfunc.so.1.0 getcfunc.o
5.3mv libgetcfunc.so.1 libgetcfunc.so
6動態指定native的動態類進行訪問C動態庫(libhardwareInfo2.so)
package com.sec.iot.service.impl;
import com.sec.iot.jni.GetCFuncJni;
import com.sec.iot.service.GetCFuncService;
import com.sec.iot.util.NativeLoader;
import org.springframework.stereotype.Service;
@Service
public class GetCFuncImpl implements GetCFuncService {
/**
* 只加載一次
*/
static{
//根據操作系統判斷,如果是linux系統則加載c++方法庫
String systemType = System.getProperty("os.name");
String ext = (systemType.toLowerCase().indexOf("win") != -1) ? ".dll" : ".so";
if(ext.equals(".so")) {
try {
//加載項目下的native文件,DLL或SO
NativeLoader.loader( "native" );
} catch (Exception e) {
System.out.println("加載so庫失敗");
}
}
}
@Override
public int start4G() {
return GetCFuncJni.start4G();
}
@Override
public String get4GInfo() {
return GetCFuncJni.get4GInfo();
}
@Override
public int setWiredNetworkStaticIp(String ip, String netmask,String gw, String dns1,
String dns2) {
return GetCFuncJni.setWiredNetworkStaticIp(ip, netmask, gw, dns1, dns2);
}
}
package com.sec.iot.util;
import java.io.*;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* 加載動態庫
* @author maling
*
*/
public class NativeLoader {
/**
* 加載項目下的native文件,DLL或SO
*
* @param dirPath 需要掃描的文件路徑,項目下的相對路徑
* @throws IOException
* @throws ClassNotFoundException
*/
public synchronized static void loader(String dirPath) throws IOException, ClassNotFoundException {
//獲取native路徑下文件目錄
Enumeration<URL> dir = Thread.currentThread().getContextClassLoader().getResources(dirPath);
// 獲取操作系統類型
String systemType = System.getProperty("os.name");
//String systemArch = System.getProperty("os.arch");
// 獲取動態鏈接庫後綴名
String ext = (systemType.toLowerCase().indexOf("win") != -1) ? ".dll" : ".so";
while (dir.hasMoreElements()) {//遍歷獲取native的每一個路徑
URL url = dir.nextElement();
System.out.println("path="+url.getPath());
//文件類型 so在window 文件 在Linux j中ar
String protocol = url.getProtocol();
if ("jar".equals(protocol)) {
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
JarFile jarFile = jarURLConnection.getJarFile();
// 遍歷Jar包
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
String entityName = jarEntry.getName();
if (jarEntry.isDirectory() || !entityName.startsWith(dirPath)) {
continue;
}
if (entityName.endsWith(ext)) {
loadJarNative(jarEntry);
}
}
} else if ("file".equals(protocol)) {
File file = new File(url.getPath());
loadFileNative(file, ext);
}
}
}
/**
* 加載native文件
* @param file
* @param ext
*/
private static void loadFileNative(File file, String ext) {
if (null == file) {
return;
}
if (file.isDirectory()) {
File[] files = file.listFiles();
if (null != files) {
for (File f : files) {
loadFileNative(f, ext);
}
}
}
if (file.canRead() && file.getName().endsWith(ext)) {
try {
System.load(file.getPath());
System.out.println("加載native文件 :" + file + "成功!!");
} catch (UnsatisfiedLinkError e) {
System.out.println("加載native文件 :" + file + "失敗!!請確認操作系統是X86還是X64!!!");
}
}
}
/**
* @throws IOException
* @throws ClassNotFoundException
* @Title: scanJ
* @Description 掃描Jar包下所有class
*/
/**
* 創建動態鏈接庫緩存文件,然後加載資源文件
*
* @param jarEntry
* @throws IOException
* @throws ClassNotFoundException
*/
private static void loadJarNative(JarEntry jarEntry) throws IOException, ClassNotFoundException {
File path = new File(".");
//將所有動態鏈接庫dll/so文件都放在一個臨時文件夾下,然後進行加載
//這是應爲項目爲可執行jar文件的時候不能很方便的掃描裏面文件
//此目錄放置在與項目同目錄下的natives文件夾下
String rootOutputPath = path.getAbsoluteFile().getParent() + File.separator;
String entityName = jarEntry.getName();
String fileName = entityName.substring(entityName.lastIndexOf("/") + 1);
System.out.println(entityName);
System.out.println(fileName);
File tempFile = new File(rootOutputPath + File.separator + entityName);
// 如果緩存文件路徑不存在,則創建路徑
if (!tempFile.getParentFile().exists()) {
tempFile.getParentFile().mkdirs();
}
// 如果緩存文件存在,直接使用此文件
if (tempFile.exists()) {
// tempFile.delete();
}
if (!tempFile.exists()) {//不存在則創建
InputStream in = null;
BufferedInputStream reader = null;
FileOutputStream writer = null;
try {
//讀取文件形成輸入流
in = NativeLoader.class.getResourceAsStream(entityName);
if (in == null) {
in = NativeLoader.class.getResourceAsStream("/" + entityName);
if (null == in) {
return;
}
}
NativeLoader.class.getResource(fileName);
reader = new BufferedInputStream(in);
writer = new FileOutputStream(tempFile);
byte[] buffer = new byte[1024];
while (reader.read(buffer) > 0) {
writer.write(buffer);
buffer = new byte[1024];
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (in != null) {
in.close();
}
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
try {
System.load(tempFile.getPath());
System.out.println("加載native文件 :" + tempFile + "成功!!");
} catch (UnsatisfiedLinkError e) {
System.out.println("加載native文件 :" + tempFile + "失敗!!請確認操作系統是X86還是X64!!!");
}
}
}
7運行結果