Pass Protobuf through JNI(C++ to Java)
项目中遇到android(java)层用到了C++库,C++返回protobuf结构给java
1. Protobuf环境安装
cd path-to-source-into-java
mvn install # 编译出jar包, copy到java工程的jniLib目录
protoc --java_out=./ com/sh/process/myproto.proto # 编译出java文件, copy到工程目录proto下
(Note: .proto文件中package com.sh.process.proto, 代表最终生成的目录和java中的package信息,需要匹配)
2. In jni: protobuf->byte[]
jbyteArray JNIEXPORT JNICALL Java_com_sh_process(JNIEnv *env, jobject thiz) {
MyProto res = GetProtobufFromC();
jbyteArray res_byte;
int size = res.ByteSize();
if(size <= 0) return res_byte; // Android 6.0 will throw exception when calling SetByteArrayRegion if size=0
void *buffer = malloc(size);
res.SerializeToArray(buffer, size); // 序列化
jbyteArray res_byte = env->NewByteArray(size); // construce jbarray[]
env->SetByteArrayRegion(res_byte, 0, size, (jbyte *)buffer); // copy result to java layer
free(buffer); // 注意:内存泄漏
return res_byte;
}
上述的方式,仍然有内存泄漏,返回了res_byte是new出来的,这个应该交给java管理:
jobject data_t0 = env->NewDirectByteBuffer((void *)proto_res.c_str(), proto_res.size());
if (data_t0) {
jclass jclsJNILib = env->FindClass("com/test/MyJNILib");
jmethodID methodID_setOutputData =
env->GetMethodID(jclsJNILib, "setOutputData", "(Ljava/nio/ByteBuffer;)V");
env->CallVoidMethod(obj, methodID_setOutputData, data_t0);
}
env->DeleteLocalRef(data_t0);
// Java端:
// call from native jni
public void setOutputData(ByteBuffer data)
{
if (data == null) {
data = ByteBuffer.wrap(new byte[]{});
System.out.println("debug: date == null");
return;
}
if (data.capacity() < 1) {
data = ByteBuffer.wrap(new byte[]{});
System.out.println("debug: data.capacity() < 1");
return;
}
jni_data = ByteBuffer.allocateDirect(data.capacity());
data.rewind();
jni_data.put(data);
data.rewind();
jni_data.flip();
}
3. In java: byte[]->protobuf
byte[] res_byte = process();
MyProto res = MyProto.parseFrom(res_byte); // 反序列化
if (res.getStatus() == MyProto.Status.SUCCESS) {
String res_str = res.getNumber();
// do sth
} else {
// do sth
}