重点内容##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