C++11 JNI开发中RAII的应用(三)--JavaClassMirror

以下是我以前写的将一个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开发的相关内容就写完了。后续如果有更多的内容,还可能会继续补充。

你可能感兴趣的:(c/c++/c++11)