Java使用JNI調用C動態庫

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運行結果

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章