近期提供给JAVA应用使用的编解码协议库,需求就是编码的字符串,需要解码为Java对象;回应消息的Java对象,需要编码为指定格式的字符串,把模板代码总结下,主要涉及几个点:
1、在JNI层找到Java对象,及其属性变量的值,然后转换为JNI层的内存数据;
2、在JNI层实例化Java对象,并设置这个Java对象的属性变量,并返回这个Java对象;
示例代码如下:
Java对象
public class TestKey {
public String name;
public String desc;
public int usage;
public String keySpec;
public String expire;
public String source;
public String credential_username;
public String credential_password;
}
JNI层实例化TestKey:
static jobject get_jobject(JNIEnv *env, const char *object_name){
if (env == NULL || object_name == NULL){
return NULL;
}
jclass clazz = NULL;
jobject jobj = NULL;
jmethodID mid_construct = NULL;
//1. 获取Person类的Class引用
clazz = env->FindClass(object_name);
if(clazz == NULL){
LOGD("clazz null");
return NULL;
}
//2. 获取类的默认构造函数ID
mid_construct = env->GetMethodID(clazz, "<init>", "()V");
if(mid_construct == NULL){
LOGD("construct null");
return NULL;
}
//3.实例化这个对象
jobj = env->NewObject(clazz, mid_construct);
if(jobj == NULL){
LOGD("jobject null");
return NULL;
}
return jobj;
}
JNIEXPORT jobject JNICALL Java_com_xxx_kmip_ProtocolUtil_decodeTestKeyRequestMessage
(JNIEnv *env, jclass obj, jbyteArray j_input){
jclass clazz = NULL;
jobject jobj = NULL;
jfieldID fid_name = NULL;
jfieldID fid_desc = NULL;
jfieldID fid_usage = NULL;
jfieldID fid_keySpec = NULL;
jfieldID fid_expire = NULL;
jfieldID fid_source = NULL;
jfieldID fid_credential_name = NULL;
jfieldID fid_credential_passwd = NULL;
env->GetJavaVM(&javaVM);
char* decode_str = (char*)env->GetByteArrayElements(j_input, 0);
int length = env->GetArrayLength(j_input);
char name[32] = {'\0'};
char description[255]= {'\0'};
char cryptographic_algorithm[128]= {'\0'};
int cryptographic_algorithm_length = 0;
int cryptographic_usage_mask = 0;
char source[128]= {'\0'};
//中间省略赋值过程
//1. 找到请求Class的引用
clazz = env->FindClass("com/test/api/entity/TestKey");
if(clazz == NULL){
LOGD("clazz null");
return NULL;
}
//2. 获取实例方法ID和变量ID
fid_name = env->GetFieldID(clazz, "name", "Ljava/lang/String;");
fid_keySpec = env->GetFieldID(clazz, "keySpec", "Ljava/lang/String;");
fid_desc = env->GetFieldID(clazz, "desc", "Ljava/lang/String;");
fid_expire = env->GetFieldID(clazz, "expire", "Ljava/lang/String;");
fid_source = env->GetFieldID(clazz, "source", "Ljava/lang/String;");
fid_usage = env->GetFieldID(clazz, "usage", "I");
fid_credential_name = env->GetFieldID(clazz, "credential_username", "Ljava/lang/String;");
fid_credential_passwd = env->GetFieldID(clazz, "credential_password", "Ljava/lang/String;");
if(fid_name==NULL
|| fid_keySpec==NULL
|| fid_desc==NULL
|| fid_source==NULL
|| fid_usage==NULL){
LOGD("age|name null");
return NULL;
}
//获取输出对象
jobj = get_jobject(env, "com/test/api/entity/TestKey");
if (jobj == NULL){
return NULL;
}
//赋值
env->SetIntField(jobj, fid_usage, 2);
jstring j_name = env->NewStringUTF(name);
env->SetObjectField(jobj, fid_name, j_name);
jstring j_desc= env->NewStringUTF(description);
env->SetObjectField(jobj, fid_desc, j_desc);
jstring j_spec = env->NewStringUTF(cryptographic_algorithm);
env->SetObjectField(jobj, fid_keySpec, j_spec);
//释放内存
env->ReleaseByteArrayElements(j_input, (jbyte*)decode_str, JNI_ABORT);
kmip_free_request_message(&ctx, &req_msg_client);
return jobj;
}
Java对象,输入到JNI层使用
public class CommonResponseMessage{
`public int return_code;
public int result_reason;
public String error_message;
public String uniq_id;
`
}
JNI层使用Java对象
//输入java对象,输出byte[]数组
JNIEXPORT jbyteArray JNICALL Java_com_xxx_ProtocolUtil_encodeTestKeyResponseMessage
(JNIEnv *env, jclass obj, jobject j_input, jint op_code){
if (j_input == NULL){
return NULL;
}
jclass clazz = NULL;
jfieldID fid_return_code = NULL;
jfieldID fid_uniq_id = NULL;
jfieldID fid_error_message = NULL;
jfieldID fid_result_reason = NULL;
env->GetJavaVM(&javaVM);
//1. 获取Class引用
clazz = env->FindClass("com/test/api/entity/CommonResponseMessage");
if(clazz == NULL){
LOGD("clazz null");
return NULL;
}
//2. 获取实例方法ID和变量ID
fid_uniq_id = env->GetFieldID(clazz, "uniq_id", "Ljava/lang/String;");
fid_error_message = env->GetFieldID(clazz, "error_message", "Ljava/lang/String;");
fid_return_code = env->GetFieldID(clazz, "return_code", "I");
fid_result_reason = env->GetFieldID(clazz, "result_reason", "I");
if(fid_uniq_id == NULL || fid_return_code==NULL || fid_error_message==NULL || fid_result_reason == NULL){
LOGD("age|name null");
return NULL;
}
//3.得到对象值
jint return_code = env->GetIntField(j_input, fid_return_code);
printf("return_code:%d\r\n", (int)return_code);
if (return_code != 0){
//failed.
resp_bi.result_status = KMIP_STATUS_OPERATION_FAILED;
jstring j_fid_error_message = (jstring)env->GetObjectField(j_input, fid_error_message);
const char* cstr= env->GetStringUTFChars(j_fid_error_message, NULL);
//保存cstr
env->ReleaseStringUTFChars(j_fid_error_message, cstr);
//fid_result_reason
jint result_reason = env->GetIntField(j_input, fid_result_reason);
}else{
jstring j_fid_uniq_id = (jstring)env->GetObjectField(j_input, fid_uniq_id);
const char* cstr= env->GetStringUTFChars(j_fid_uniq_id, NULL);
//保存cstr
env->ReleaseStringUTFChars(j_fid_uniq_id, cstr);
}
//....省略编码处理
return result;
}
Java 基本类型与方法签名中参数类型和返回值类型的映射关系如下:
内存使用,关于局部引用和全局引用,讲的还是蛮清晰的:http://www.itpub.net/2020/01/02/4987/
Native层返回的jobject对象和引用是否需要在native层销毁,这篇文章有说:jni中的NewStringUTF这个函数调用后需要释放内存吗?