以下是我以前写的将一个C++对象转成java对象的函数toJCodeBean
。
static jobject toJCodeBean(JNIEnv* env, const code_bean& bean) {
auto code_bean_class =jni_utilits::raii_FindClass_LocalRef("Lnet/gdface/facedbsdk/local/CodeCacheManager$CodeBean;");
auto constructor = env->GetMethodID(code_bean_class.get(), "" , "()V");
auto obj = env->NewObject(code_bean_class.get(), constructor);
auto field_id = env->GetFieldID(code_bean_class.get(), "id", "[B");
env->SetObjectField(obj, field_id, jni_utilits::tojbytearray((jbyte*) (((&bean.id))), (jsize) ((sizeof(bean.id)))).get());
auto field_code = env->GetFieldID(code_bean_class.get(), "code", "[B");
env->SetObjectField(obj, field_code,
jni_utilits::tojbytearray((jbyte*) &(FACE_CODE_CONVERT(bean.code)), (jsize) sizeof(bean.code)).get());
auto field_imgMD5 = env->GetFieldID(code_bean_class.get(), "imgMD5", "Ljava/lang/String;");
env->SetObjectField(obj, field_imgMD5, MD5toJString(bean.imgMD5).get());
auto field_similarity = env->GetFieldID(code_bean_class.get(), "similarity", "F");
env->SetFloatField(code_bean_class.get(), field_similarity, (jfloat) (((bean.similarity))));
return obj;
}
如果这段代码被频繁调用,那么每次都要重复的去调用JNIEnv::FindClass,通过字符串去查找jobject,每次都要调用GetFieldID通过字符串查找获取FieldID。对是一个java class,这都是常量啊,为什么不可以一开始把这些值都记下来,每次使用时直接取这个值就行了?
于是,在前面《C++11 JNI开发中RAII的应用(一)–制作基础工具》和《C++11 JNI开发中RAII的应用(二)–JNI函数封装》两节的基础之上,我决定做一个JavaClassMirror类记录一个类的这些常量,用于后面的频繁调用。
/* java类与C++类的镜像,便于操作java对象 */
class JavaClassMirror{
public:
/* raii_var类变量,java class 的全局引用,JavaClassMirror析构的时候会自动释放全局引用*/
raii_var javaclass;
/* 类构造函数的 jmethodID */
jmethodID constructor;
/* 方法名与jfieldID的映射,可以通过方法名查找到对应的jfieldID,不支持重载的多个方法*/
unordered_map<string,jfieldID> field;
/* 根据类名,构造函数签名,以及 initializer_list提供的pair * 初始化类,并将 */
JavaClassMirror(string canonicalName, std::pair<string, string> constr,
std::initializer_list<std::pair<string, string> > field_signature) :
javaclass(jni_utilits::raii_FindClass_GlobalRef(canonicalName.data())),
constructor(jni_utilits::getJNIEnv()->GetMethodID(javaclass.get(), constr.first.data(), constr.second.data())) {
auto env =jni_utilits::getJNIEnv();
//根据方法名和签名获取FieldID,加入field映射中
for (auto node : field_signature) {
auto f = env->GetFieldID(javaclass.get(), node.first.data(), node.second.data());
assert(nullptr != f);
field.emplace(node.first, f);
}
}
JavaClassMirror(JavaClassMirror&&)=default;
//根据不同的数据类型提供统一的SetField/GetField方法
template<typename T>
typename std::enable_if<std::is_same::value>::type SetField(jobject obj, const char* name, T fieldObj) {
jni_utilits::getJNIEnv()->SetDoubleField(obj, field.find(name)->second, fieldObj);
}
template<typename T>
typename std::enable_if<std::is_same::value>::type SetField(jobject obj,const char* name,T fieldObj){
jni_utilits::getJNIEnv()->SetFloatField(obj,field.find(name)->second,fieldObj);
}
template<typename T>
typename std::enable_if<std::is_same::value>::type SetField(jobject obj, const char* name, T fieldObj) {
jni_utilits::getJNIEnv()->SetLongField(obj, field.find(name)->second, fieldObj);
}
template<typename T>
typename std::enable_if<std::is_same::value>::type SetField(jobject obj, const char* name, T fieldObj) {
jni_utilits::getJNIEnv()->SetIntField(obj, field.find(name)->second, fieldObj);
}
template<typename T>
typename std::enable_if<std::is_same::value>::type SetField(jobject obj, const char* name, T fieldObj) {
jni_utilits::getJNIEnv()->SetShortField(obj, field.find(name)->second, fieldObj);
}
template<typename T>
typename std::enable_if<std::is_same::value>::type SetField(jobject obj, const char* name, T fieldObj) {
jni_utilits::getJNIEnv()->SetCharField(obj, field.find(name)->second, fieldObj);
}
template<typename T>
typename std::enable_if<std::is_same::value>::type SetField(jobject obj, const char* name, T fieldObj) {
jni_utilits::getJNIEnv()->SetByteField(obj, field.find(name)->second, fieldObj);
}
template<typename T>
typename std::enable_if<std::is_same::value>::type SetField(jobject obj, const char* name, T fieldObj) {
jni_utilits::getJNIEnv()->SetBooleanField(obj, field.find(name)->second, fieldObj);
}
template<typename T>
typename std::enable_if<std::is_base_of<_jobject,typename std::remove_pointer::type>::value>::type
SetField(jobject obj, const char* name, T fieldObj) {
jni_utilits::getJNIEnv()->SetObjectField(obj, field.find(name)->second, fieldObj);
}
template<typename T>
typename std::enable_if<std::is_same::value,T>::type GetField(jobject obj,const char* name){
return jni_utilits::getJNIEnv()->GetDoubleField(obj,field.find(name)->second);
}
template<typename T>
typename std::enable_if<std::is_same::value,T>::type GetField(jobject obj,const char* name){
return jni_utilits::getJNIEnv()->GetFloatField(obj,field.find(name)->second);
}
template<typename T>
typename std::enable_if<std::is_same::value,T>::type GetField(jobject obj,const char* name){
return jni_utilits::getJNIEnv()->GetLongField(obj,field.find(name)->second);
}
template<typename T>
typename std::enable_if<std::is_same::value,T>::type GetField(jobject obj,const char* name){
return jni_utilits::getJNIEnv()->GetIntField(obj,field.find(name)->second);
}
template<typename T>
typename std::enable_if<std::is_same::value,T>::type GetField(jobject obj,const char* name){
return jni_utilits::getJNIEnv()->GetShortField(obj,field.find(name)->second);
}
template<typename T>
typename std::enable_if<std::is_same::value,T>::type GetField(jobject obj,const char* name){
return jni_utilits::getJNIEnv()->GetCharField(obj,field.find(name)->second);
}
template<typename T>
typename std::enable_if<std::is_same::value,T>::type GetField(jobject obj,const char* name){
return jni_utilits::getJNIEnv()->GetByteField(obj,field.find(name)->second);
}
template<typename T>
typename std::enable_if<std::is_same::value,T>::type GetField(jobject obj,const char* name){
return jni_utilits::getJNIEnv()->GetBooleanField(obj,field.find(name)->second);
}
template<typename T>
typename std::enable_if<std::is_base_of<_jobject,typename std::remove_pointer::type>::value,raii_var>::type
GetField(jobject obj,const char* name){
return jni_utilits::raii_GetObjectField(obj,field.find(name)->second);
}//返回raii_var封装的jobject
};
有了这个类之后,比如我们可以像这样初始化JavaClassMirror
JavaClassMirror mirror(
"Lnet/gdface/facedbsdk/local/CodeCacheManager$CodeBean;",//类名
{"" , "()V"}, //构造函数
{
{"id", "[B"},
{"code", "[B"},
{"imgMD5", "Ljava/lang/String;"},
{"similarity", "F"}
}//成员变量名及类型签名
);
最开始那段代码toJCodeBean
就可以改成这样了,多么清楚简单。
raii_var BeanUtilits::toJCodeBean(const code_bean& bean, JavaClassMirror& mirror) {
auto var = jni_utilits::raii_NewObject(mirror.javaclass.get(), mirror.constructor);
auto obj = *var;
if (nullptr != obj) {
mirror.SetField(obj, "id", MD5tojbyteArray(bean.id).get());
mirror.SetField(obj, "code",face_codetojbyteArray(FACE_CODE_CONVERT(bean.code)).get());
mirror.SetField(obj, "imgMD5", MD5toJString(bean.imgMD5).get());
mirror.SetField(obj, "similarity", (jfloat) (bean.similarity));
}
return var;
}
当然JavaClassMirror功能并不完整,并不能满足所有JNI开发的需要,只是在我的项目中这样做已经够了,自己完全可以按这个思路根据需要把更多的功能封装到这里。到这里关于C++11下JNI开发的相关内容就写完了。后续如果有更多的内容,还可能会继续补充。