Java使用JNI调用C动态库

jni基本概念不在介绍,本文介绍基于springboot项目,在自己写的动态库中去调用其他C动态库。

1项目结构

代码下载:jni-util.zip-Java工具类资源-CSDN下载

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运行结果

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码灵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值