Java通过JNI调用Linux下C库详解
1. 编译环境准备
1.1 linux-C/C++编译环境
需要根据实际需要,安装编译环境,gcc 或者 g++。
1.2 linux-java编译环境
安装java环境,需要java/javah/javac可执行。
2. java调用JNI中间件编写(java<->JNI)
2.1 创建java文件TestJNI.java
如果通过maven创建工程,输入对应的groupId,在对应的路径下创建java文件。
假如路径是:cn.cmguan.saas.device.dev.test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package cn.cmguan.saas.device.dev.test;
public class TestJNI{
static { System.loadLibrary("JNITest"); }
public static native int testSum(int i, int j);
public static native String getDyPwdB(String mac); public static native String getDyPwdK(String key);
public static native String getEnByN(String pId,String ts); public static native String getEnByM(String mac,String ts); }
|
System.loadLibrary(“JNITest”);里面引用的JNITest库是后续所需的C动态库:libJNITest.so
linux下静态库是:libxxx.a
linux下动态库是:libxxx.so
2.2 生成JNI调用中间件的.h头文件
- 生成对应的class文件
`javac -encoding utf-8 TestJNI.java`
生成:TestJNI.class
- 生成对应包名的.h文件
到工程的主目录java下:
`javah cn.cmguan.saas.device.dev.test.TestJNI`
生成:cn\_cmguan\_saas\_device\_dev\_test\_TestJNI.h
2.3 外部调用中间件
外部对TestJNI.java的调用,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class RunTest {
public static void main(String[] args) { System.out.println("start>>>>"); int num = TestJNI.testSum(10,33); System.out.println("testSum: "+num); String s1 = TestJNI.getDyPwdB("a910e107004b1200"); System.out.println("getDyPwdB: "+s1); String s2 = TestJNI.getDyPwdK("zzweiyhdd11213123"); System.out.println("getDyPwdK: "+s2); String s3 = TestJNI.getEnByN("0000000000802417","1644389492"); System.out.println("getEnByN: "+s3); String s4 = TestJNI.getEnByM("a910e107004b1200","1644389495"); System.out.println("getEnByM: "+s4); System.out.println("end>>>>"); } }
|
3. JNI调用的C代码实现(JNI<->C)
3.1 C代码头文件
在上述步骤完成后,我们可以看到生成的头文件cn_cmguan_saas_device_dev_test_TestJNI.h代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include <jni.h>
#ifndef _Included_cn_cmguan_saas_device_dev_test_TestJNI #define _Included_cn_cmguan_saas_device_dev_test_TestJNI #ifdef __cplusplus extern "C" { #endif
JNIEXPORT jint JNICALL Java_cn_cmguan_saas_device_dev_test_TestJNI_testSum (JNIEnv *, jclass, jint, jint);
JNIEXPORT jstring JNICALL Java_cn_cmguan_saas_device_dev_test_TestJNI_getDyPwdB (JNIEnv *, jclass, jstring);
JNIEXPORT jstring JNICALL Java_cn_cmguan_saas_device_dev_test_TestJNI_getDyPwdK (JNIEnv *, jclass, jstring);
JNIEXPORT jstring JNICALL Java_cn_cmguan_saas_device_dev_test_TestJNI_getEnByN (JNIEnv *, jclass, jstring, jstring);
JNIEXPORT jstring JNICALL Java_cn_cmguan_saas_device_dev_test_TestJNI_getEnByM (JNIEnv *, jclass, jstring, jstring);
#ifdef __cplusplus } #endif #endif
|
3.2 C代码实现
接下来创建对应的C文件:cn_cmguan_saas_device_dev_test_TestJNI.c
假如我们在这个c函数里面需要实现其他复杂的功能,需要多文件情况下,就创建多个c文件。
例如:cJniTest.h, cJniTest.c
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include "cJniTest.h"
char* operation(char* str1,char* str2) { char tmpBuff[128] = {0}; sprintf(tmpBuff,"%s-%s",str1,str2); return tmpBuff; }
|
然后就是对头文件中函数的实现。
我们以两个函数为例:
3.2.1 Java_cn_cmguan_saas_device_dev_test_TestJNI_testSum
1 2 3 4 5 6 7
| JNIEXPORT jint JNICALL Java_cn_cmguan_saas_device_dev_test_TestJNI_testSum (JNIEnv * jEnv, jclass c, jint i, jint j) { printf("Java_cn_cmguan_saas_device_dev_test_TestJNI_testSum start >>>>>>>>>>>>>>>>\n"); return (i+j); }
|
3.2.2 Java_cn_cmguan_saas_device_dev_test_TestJNI_getEnByN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| JNIEXPORT jstring JNICALL Java_cn_cmguan_saas_device_dev_test_TestJNI_getEnByN (JNIEnv *jEnv, jclass c, jstring pId, jstring times) { printf("Java_cn_cmguan_saas_device_dev_test_TestJNI_getEnByN start >>>>>>>>>>>>>>>>\n"); uint8_t *perID = (uint8_t *)(*jEnv)->GetStringUTFChars(jEnv, pId, 0); if( NULL == perID ) { return NULL; } uint8_t *timestamp = (uint8_t *)(*jEnv)->GetStringUTFChars(jEnv, times, 0); if( NULL == timestamp ) { return NULL; } uint8_t cPersonID[16]; memset(cPersonID,0,sizeof(cPersonID)); strncpy(cPersonID,perID,sizeof(cPersonID));
uint8_t ctt[10]; memset(ctt,0,sizeof(ctt)); strncpy(ctt,timestamp,sizeof(ctt));
(*jEnv)->ReleaseStringUTFChars(jEnv, pId, personID); (*jEnv)->ReleaseStringUTFChars(jEnv, times, timestamp);
char* tmpBuff = operation(cPersonID,ctt);
return (*jEnv)->NewStringUTF(jEnv,tmpBuff); }
|
4. C动态库编译
java通过jni调用可实现对c动态库的调用。
通过gcc 编译c动态库:(-rdynamic -shared -fPIC)
1 2 3 4
| CFLAGS_SO=-Wall -Os -rdynamic -shared -fPIC INCLUDE=-I./ -I/usr/java/jdk1.8.0_311-amd64/include -I/usr/java/jdk1.8.0_311-amd64/include/linux
so: gcc $(CFLAGS_SO) $(INCLUDE) cn_cmguan_saas_device_dev_test_TestJNI.c cJniTest.c -o libJNITest.so
|
make so
5. java执行测试
将java程序生成对应的jar文件后,把生成的C动态库放到java执行的当前目录。
执行jar程序:
java -Djava.library.path='.' -classpath TestJNI-1.0-SNAPSHOT.jar "cn.cmguan.saas.device.dev.test.RunTest"
即可实现java对C库的调用。