如何通过JNI C++获取Java接口枚举值并转换为UE4枚举
解决AdMob JNI调用getInitializationState崩溃并转换枚举到UE4的方案
崩溃原因
直接通过JNI调用getInitializationState触发java.lang.NoSuchMethodError,核心原因是实际返回的AdapterStatus实现类是混淆后的内部类com.google.android.gms.internal.ads.zzbrt,该类的方法签名或名称与公开API的AdapterStatus接口不一致,常规JNI方法调用无法匹配到对应方法。
可行解决方案
方案1:Java层提前转换枚举为int再传递
修改Java端回调逻辑,先将AdapterStatus的State枚举转为int值,再传递给JNI,彻底避免C++层处理复杂的JNI枚举调用:
// Java端代码示例 public class AdMobInitializer { private void onInitializationComplete(Map<String, AdapterStatus> statusMap) { HashMap<String, Integer> stateIntMap = new HashMap<>(); for (Map.Entry<String, AdapterStatus> entry : statusMap.entrySet()) { // 直接取枚举的ordinal值,若枚举有自定义getValue()方法可替换为该方法 int stateInt = entry.getValue().getInitializationState().ordinal(); stateIntMap.put(entry.getKey(), stateInt); } // 调用JNI方法传递转换后的int集合 nativeOnInitializationResponse(stateIntMap); } private native void nativeOnInitializationResponse(Map<String, Integer> stateIntMap); }
C++层直接读取int值并映射到UE4枚举:
// C++ JNI实现示例 JNIEXPORT void JNICALL Java_com_yourpackage_AdMobInitializer_nativeOnInitializationResponse( JNIEnv* env, jobject thiz, jobject stateIntMap) { jclass hashMapCls = env->FindClass("java/util/HashMap"); jmethodID entrySetMethod = env->GetMethodID(hashMapCls, "entrySet", "()Ljava/util/Set;"); jobject entrySet = env->CallObjectMethod(stateIntMap, entrySetMethod); jclass setCls = env->FindClass("java/util/Set"); jmethodID iteratorMethod = env->GetMethodID(setCls, "iterator", "()Ljava/util/Iterator;"); jobject iterator = env->CallObjectMethod(entrySet, iteratorMethod); jclass iteratorCls = env->FindClass("java/util/Iterator"); jmethodID hasNextMethod = env->GetMethodID(iteratorCls, "hasNext", "()Z"); jmethodID nextMethod = env->GetMethodID(iteratorCls, "next", "()Ljava/lang/Object;"); jclass entryCls = env->FindClass("java/util/Map$Entry"); jmethodID getKeyMethod = env->GetMethodID(entryCls, "getKey", "()Ljava/lang/Object;"); jmethodID getValueMethod = env->GetMethodID(entryCls, "getValue", "()Ljava/lang/Object;"); while (env->CallBooleanMethod(iterator, hasNextMethod)) { jobject entry = env->CallObjectMethod(iterator, nextMethod); jstring adapterName = (jstring)env->CallObjectMethod(entry, getKeyMethod); jint stateInt = env->CallIntMethod(entry, getValueMethod); // 转换为UE4枚举 EAdMobInitializationState ueState = static_cast<EAdMobInitializationState>(stateInt); // 后续业务逻辑处理... env->DeleteLocalRef(adapterName); env->DeleteLocalRef(entry); } // 释放局部引用 env->DeleteLocalRef(iterator); env->DeleteLocalRef(entrySet); env->DeleteLocalRef(hashMapCls); env->DeleteLocalRef(setCls); env->DeleteLocalRef(iteratorCls); env->DeleteLocalRef(entryCls); }
方案2:JNI反射获取枚举的int值
若必须在C++层处理原始AdapterStatus Map,可通过反射调用方法并提取枚举的int值:
// C++中处理单个AdapterStatus对象的逻辑示例 jobject adapterStatus = ...; // 从Map中取出的AdapterStatus实例 jclass adapterStatusCls = env->GetObjectClass(adapterStatus); // 反射调用getInitializationState方法 jmethodID getStateMethod = env->GetMethodID(adapterStatusCls, "getInitializationState", "()Lcom/google/android/gms/ads/initialization/InitializationState;"); jobject stateEnum = env->CallObjectMethod(adapterStatus, getStateMethod); // 获取枚举的ordinal值(若枚举有自定义值方法,替换为对应方法名) jclass enumCls = env->FindClass("java/lang/Enum"); jmethodID ordinalMethod = env->GetMethodID(enumCls, "ordinal", "()I"); jint stateInt = env->CallIntMethod(stateEnum, ordinalMethod); // 转换为UE4枚举 EAdMobInitializationState ueState = static_cast<EAdMobInitializationState>(stateInt); // 释放局部引用 env->DeleteLocalRef(adapterStatusCls); env->DeleteLocalRef(stateEnum); env->DeleteLocalRef(enumCls);
方案3:直接读取混淆类的字段值
若反射调用方法仍失败,可尝试直接读取zzbrt类中存储状态的字段(需反编译AdMob SDK确认实际字段名,比如zzb、zzc等):
// 假设混淆后的状态字段名为zzb(需反编译验证) jclass zzbrtCls = env->GetObjectClass(adapterStatus); jfieldID stateField = env->GetFieldID(zzbrtCls, "zzb", "I"); jint stateInt = env->GetIntField(adapterStatus, stateField); // 转换为UE4枚举 EAdMobInitializationState ueState = static_cast<EAdMobInitializationState>(stateInt); env->DeleteLocalRef(zzbrtCls);
注意:该方案依赖AdMob内部实现细节,SDK更新后可能失效,仅作为应急方案使用。
优先级建议
优先采用方案1,Java层转换的方式最稳定,完全规避对AdMob内部实现的依赖;方案2次之,依赖公开API的方法签名,兼容性较好;方案3仅在极端场景下使用。
内容的提问来源于stack exchange,提问作者FunnyCOFE
相关产品推荐
相关产品推荐

