Jni开发(一)编写Jni头文件
之前简单的写过一篇文章《Android Studio-Jni开发》,介绍的是如何在AndroidStudio上进行Jni开发,使用CMakeLists进行配置并生成so。本次介绍如何使用Makefile编译C代码生成so,并在Linux系统上运行测试代码。
生成Jni头文件
生成Jni头文件,如果方法较多的话,我使用的AndroidStudio进行快速生成。
一、创建native方法
创建SDFAPI.java 类文件:
//-----------------------------设备管理类---------------------------------------
public native int OpenDevice(long[] phDeviceHandle);
public native int OpenDeviceByIndex(int devIndex, long[] phDeviceHandle);
public native int CloseDevice(long hDeviceHandle);
public native int OpenSession(long hDeviceHandle, long[] phSessionHandle);
public native int CloseSession(long hSessionHandle);
public native int GetDeviceInfo(long hSessionHandle, DeviceInfo pstDeviceInfo);
public native int GenerateRandom(long hSessionHandle, int uiLength, byte[] pucRandom);
public native int GetPrivateKeyAccessRight(long hSessionHandle, int uiKeyIndex, byte[] pucPassword, int uiPwdLength);
public native int ReleasePrivateKeyAccessRight(long hSessionHandle, int uiKeyIndex);
二、 实现native方法
如图,未实现的native方法,方法名称会变红,按alt+enter自动生成jni文件sdfapi.c。
spiapi.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include "sdfapi.h"
#include "tool.h"
JNIEXPORT jint JNICALL
Java_com_dream_test_SDFAPI_OpenDevice(JNIEnv *env, jobject thiz, jlongArray ph_device_handle) {
int ret;
jlong dev[] = {0};
ret = SDF_OpenDevice((void **) dev);// 所依赖库的头文件
if (!ret) {
(*env)->SetLongArrayRegion(env, ph_device_handle, 0, 1, dev);
}
return c2j_errCode(ret);// 返回错误码
}
JNIEXPORT jint JNICALL
Java_com_dream_test_SDFAPI_GetDeviceInfo(JNIEnv *env, jobject thiz, jlong h_session_handle, jobject pst_device_info) {
int ret;
DEVICEINFO devi;// 实体类对应的C结构体
jclass cls;
const char *csi = "com/dream/test/DeviceInfo";// 实体类包名
if (!pst_device_info) {// 判断输入参数不为空
//throw_null_pointer_exception(env, "pst_device_info");// 抛出异常信息
goto err1;
}
memset(&devi, 0x0, sizeof(devi));
ret = SDF_GetDeviceInfo((void *) h_session_handle, &devi);
if (!ret) {
cls = (*env)->FindClass(env, csi);
if (!cls) {
//throw_class_not_found_exception(env, csi);
goto err0;
}
// 结构体 devi 中数据 赋值给 java实体类 pst_device_info 。下列自定义转换方法写在tool.c中以便引用
ret = set_byte_array(env, cls, csi, pst_device_info, "setIssuerName", (jbyte *) devi.IssuerName, sizeof(devi.IssuerName));
if (ret)
goto err0;
ret = set_byte_array(env, cls, csi, pst_device_info, "setDeviceName", (jbyte *) devi.DeviceName, sizeof(devi.DeviceName));
if (ret)
goto err0;
ret = set_byte_array(env, cls, csi, pst_device_info, "setDeviceSerial", (jbyte *) devi.DeviceSerial, sizeof(devi.DeviceSerial));
if (ret)
goto err0;
goto err0;
ret = set_int(env, cls, csi, pst_device_info, "setDeviceVersion", devi.DeviceVersion);
if (ret)
goto err0;
ret = set_int(env, cls, csi, pst_device_info, "setStandardVersion", devi.StandardVersion);
if (ret)
goto err0;
ret = set_int_array(env, cls, csi, pst_device_info, "setAsymAlgAbility", (jint *) devi.AsymAlgAbility,
sizeof(devi.AsymAlgAbility) / sizeof(devi.AsymAlgAbility[0]));
if (ret)
goto err0;
ret = set_int(env, cls, csi, pst_device_info, "setSymAlgAbility", devi.SymAlgAbility);
if (ret)
goto err0;
ret = set_int(env, cls, csi, pst_device_info, "setHashAlgAbility", devi.HashAlgAbility);
if (ret)
goto err0;
ret = set_int(env, cls, csi, pst_device_info, "setBufferSize", devi.BufferSize);
if (ret)
goto err0;
(*env)->DeleteLocalRef(env, cls);// jni.h方法
}
return c2j_errCode(ret);
err0:
if (cls)
(*env)->DeleteLocalRef(env, cls);
return c2j_errCode(SDR_INARGERR);
err1:
return c2j_errCode(SDR_INARGERR);
}
三、java与C结构体转换的工具类
tool.c代码示例:
int set_byte_array(JNIEnv *env, jclass cls, const char *csi, jobject o, const char *ms, jbyte *dat, size_t siz) {
char err[512];
jmethodID met;
jbyteArray arr;
met = (*env)->GetMethodID(env, cls, ms, "([B)V");// 参数为byte[] 回参void
if (!met) {
//throw_no_such_method_exception(env, csi, ms);
goto err0;
}
arr = (*env)->NewByteArray(env, siz);
if (!arr) {
memset(err, 0x0, sizeof(err));
snprintf(err, sizeof(err) - 1, "(*env)->NewByteArray(%p, %lu)", env, siz);
//throw_runtime_exception(env, err);
goto err0;
}
(*env)->SetByteArrayRegion(env, arr, 0, siz, dat);
(*env)->CallVoidMethod(env, o, met, arr);
(*env)->DeleteLocalRef(env, arr);
return 0;//成功
err0:
return 1;// 失败
}
Makefile编写
# 头文件
INCS = -I.
# 源文件 我这边生成了多个so,所以引用了多个源文件
SRCS = sdfapi.c xdmapi.c tool.c Ukey.c
# 源文件编译后对应的.o文件
OBJS = $(SRCS:.c=.o)
OBJS1 = sdfapi.o tool.o
OBJS2 = xdmapi.o tool.o
OBJS3 = Ukey.o tool.o
# 编译器
CC = gcc
CFLAGS1 = -g -fPIC $(INCS)
CFLAGS2 = -shared
DEFS = -D_SOURCE
CFLAGS3 = -L./ -ltsrs10 -ldl -lpthread -Wl,-rpath="."
# 编译规则
all : libsdf.so libxdm.so libukey.so
%.o : %.c %.h
$(CC) $(CFLAGS1) $(DEFS) -c $< -o $@
libsdf.so : $(OBJS1)
$(CC) $(CFLAGS1) $(CFLAGS2) $(OBJS1) -o $@ $(CFLAGS3)
libxdm.so : $(OBJS2)
$(CC) $(CFLAGS1) $(CFLAGS2) $(OBJS2) -o $@ $(CFLAGS3)
libukey.so : $(OBJS3)
$(CC) $(CFLAGS1) $(CFLAGS2) $(OBJS3) -o $@ $(CFLAGS3)
.PHONY : clean build.c
clean :
rm -rf *.o libsdf.so libxdm.so libukey.so
使用make命令进行编译生成so文件: