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

猜你喜欢

转载自blog.csdn.net/weixin_35804181/article/details/79629422