JAVA通过JNI调用C语言库

重点内容##1.前言

  • JDK版本1.8.0
  • 操作系统 ubuntu 16.04.4
  • 目标实现JAVA调用C语言库
root@msos:/root# java -version
openjdk version "1.8.0_151"
OpenJDK Runtime Environment (build 1.8.0_151-8u151-b12-0ubuntu0.16.04.2-b12)
OpenJDK 64-Bit Server VM (build 25.151-b12, mixed mode)

##2.JNI层说明
###2.1.C语言的JNI封装例程

/*
注意:
    <1>加密狗检测间隔时间为30秒;
    <2>将授权状态将实时打印到/tmp/msauthentstatus文件;
*/
#define MSAUTHENTJNI_C
#include <stdlib.h> 
#include <stdio.h>
#include <string.h> 
#include <unistd.h>
#include <libmscommon/mscommon.h>
#include <libmslog/mslog.h>
#include "msauthent.h" 
#include "jni.h"

#define FLAG  "msauthentjni"
/*库初始化分配资源,一个项目中只需要调用一次即可。*/
 JNIEXPORT void JNICALL Java_jniTest_msauthentjni_init( JNIEnv * env,jobject thiz,jint logopt, jstring pauthent_product_jni) 
 {
	ms_string pauthent_product= (*env)->GetStringUTFChars(env, pauthent_product_jni, ms_null);
	if(pauthent_product == ms_null)  {
		return;  
	}  
	msauthent_oneapi_init(logopt,pauthent_product);
	(*env)->ReleaseStringUTFChars(env, pauthent_product_jni, pauthent_product);
}
/*获取当前授权是否匹配当前产品,是则返回真。*/
JNIEXPORT jint JNICALL Java_jniTest_msauthentjni_isproduct(JNIEnv *env, jobject obj, jstring pnamejni)  
{
	ms_string pname= (*env)->GetStringUTFChars(env, pnamejni, ms_null);
	if(pname == ms_null)  {
		return ms_false;  
	}  
	ms_bool isproduct=msauthent_oneapi_isproduct(pname);
	(*env)->ReleaseStringUTFChars(env, pnamejni, pname);
	return isproduct;
	
}  
//获取授权连接数。当授权无效或超时时,返回默认值2
 JNIEXPORT jboolean JNICALL Java_jniTest_msauthentjni_getconnect( JNIEnv * env,jobject thiz,jint debug) 
 {
	int connect=msauthent_oneapi_getconnect(debug);
	return  connect;
}
//获取授权总天数,目前仅用于显示
 JNIEXPORT jint JNICALL Java_jniTest_msauthentjni_getvaliddays( JNIEnv * env,jobject thiz,jint debug) 
 {
	int validdays=msauthent_oneapi_getvaliddays(debug);
	return  validdays;
}
//获取授权剩余时间,单位小时,目前仅用于显示
 JNIEXPORT jint JNICALL Java_jniTest_msauthentjni_getreshours( JNIEnv * env,jobject thiz,jint debug) 
 {
	int reshours=msauthent_oneapi_getreshours(debug);
	return  reshours;
}
//库注销释放资源,一个项目中只需要调用一次
 JNIEXPORT void JNICALL Java_jniTest_msauthentjni_deinit( JNIEnv * env,jobject thiz) 
 {
	msauthent_oneapi_deinit();
}

#undef MSAUTHENTJNI_C


###2.2.编译命令

gcc  src/msauthentjni.c  -DOS_LINUX_X64 -Isrc `pkg-config --cflags libmslog` `pkg-config --cflags libmscommon` `pkg-config --cflags libmstool` `pkg-config --libs libmslog` `pkg-config --libs libmscommon` `pkg-config --libs libmstool` -shared -fPIC -I/usr/lib/jdk/jdk1.8.0_161/include -I/usr/lib/jdk/jdk1.8.0_161/include/linux  `pkg-config --libs libmsauthent` `pkg-config --cflags libmsauthent`  -o out/lib/libmsauthentjni.so 

###2.3.格式类型说明

  • JNI函数固定格式

    JNIEXPORT  返回值类型   JNICALL java_package_class_nativeapi( JNIEnv * env,jobject thiz,其他参数) ;
    
  • java_package_class_nativeapi的格式

    “Java_jniTest_msauthentjni_init”中package为jniTest,class为msauthentjni,而nativeapi为init,即这个函数只能被包名为jniTest中的msauthentjni(class)调用,且函数名为init;(类似的还有Java_jniTest_msauthentjni_getconnect,Java_jniTest_msauthentjni_deinit)
    
  • ( JNIEnv * env,jobject thiz,其他参数)的格式:前两个参数必须存在,需要传递的其他参数可扩展;

  • JNI类型映射不做过多的描述,只列举几个常用的

     Java		JNI 			C/C++	                               
     boolean     jboolean      	8位整型                           
     byte		jbyte			带符号的8位整型  
     char		jchar			无符号的16位整型                   
     short		jshort			带符号的16位整型             
     int  		jint  			带符号的32位整型               
     long		jlong			带符号的64位整型
     float		jfloat			32位浮点型      
     double		jdouble			64位浮点型 
     String      jstring			字符串对象                               
    

###2.4.特别注意

  • 如果传入的参数是jstring类型,必须要进行转换,转换代码参考函数Java_jniTest_msauthentjni_getconnect中的product_num;
  • 如果返回值是jstring类型,需要调用(*env)->NewStringUTF(env,tmpstr)进行转换;
  • 如果希望传递object,那么可以借助jobject thiz这个参数,这里不再详解;

##3.JAVA层说明
###3.1.JAVA语言调用的JNI例程

package jniTest;

public class msauthentjni {
	static {
		System.loadLibrary( "msauthentjni" );
		
	}


	public static native void  init( int logopt,String authent_product); 
	public static native boolean   isproduct(String name);
	public static native int   getconnect(int debug);
	public static native int   getvaliddays(int debug);
	public static native int   getreshours(int debug);
	public static native void  deinit(); 
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		init(52,"msdx_edu");	//一个项目中只能调用一次

		boolean isproduct=isproduct("MS-DXEDU");
		if(true==isproduct) {
			System.out.print("------------right product\r\n");
			int validdays=getvaliddays(0);
			int reshours=getreshours(0);
			int connect=getconnect(0);
			System.out.print("------------connect:"+connect+",validdays:"+validdays+",reshours:"+reshours+"\r\n");
		}else {
			System.out.print("------------Unknow product\r\n");
		}
		deinit();	//一个项目中只能调用一次
	}
}

###3.2.说明

  • 库的加载使用System.loadLibrary( “msauthentjni” ),只需要加载最直接的库即可;
  • 对于nativeapi为init的调用,完全吻合2.3所述
    ##4. 错误列表
    ###4. 1.环境错误,错误信息如下
`Exception in thread "main" java.lang.UnsatisfiedLinkError: no msauthentjni in java.library.path`
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
	at java.lang.Runtime.loadLibrary0(Runtime.java:870)
	at java.lang.System.loadLibrary(System.java:1122)
	`at jniTest.msauthentjni.<clinit>(msauthentjni.java:5)`

问题原因:libmsauthentjni.so库不在ECLIPSE的标准库路进下;
解决方法:将libmsauthentjni.so的库路径(我的是/usr/local/lib/)加入到环境变量LD_LIBRARY_PATH,然后再其中ECLIPSE程序。

`export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH:/usr/local/lib/`

###4. 2.JNI函数名错误,错误信息如下

`Exception in thread "main" java.lang.UnsatisfiedLinkError: jniTest.msauthentjni.init(I)V
	at jniTest.msauthentjni.init(Native Method)
	at jniTest.msauthentjni.main(msauthentjni.java:15)`

问题原因:jni函数名中的包名或者CLASS和调用的不匹配,详细见本文2.3描述;
解决方法:修改jni函数名中的包名或者CLASS

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

酷咪哥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值