我有以下代码:
JNI调用包装器:
/// <summary>
/// Simple JNI class wrapper
/// </summary>
struct SimpleJNIClass{
JavaVM* vm = nullptr;
jobject instanceRef = nullptr;
JNIEnv *lockedEnv = nullptr;
SimpleJNIClass(JNIEnv *env, jobject instanceRef){
//Returns the Java VM interface (used in the Invocation API) associated with the current thread.
//The result is placed at the location pointed to by the second argument, vm.
env->GetJavaVM(&this->vm);
this->instanceRef = (jobject)env->NewGlobalRef(instanceRef);
};
SimpleJNIClass(JNIEnv * env){
env->GetJavaVM(&this->vm);
};
virtual ~SimpleJNIClass() {
if (this->vm == nullptr) {
return;
}
if (JNIEnv * env = GetEnv()){
env->DeleteGlobalRef(instanceRef);
instanceRef = nullptr;
FreeEnv(env);
}
};
JNIEnv * LockEnv() {
this->lockedEnv = this->GetEnv();
return this->lockedEnv;
}
void UnLockEnv() {
this->lockedEnv = nullptr;
this->FreeEnv(this->lockedEnv);
}
JNIEnv * GetEnv(){
if (this->lockedEnv != nullptr) {
return this->lockedEnv;
}
JNIEnv * env = nullptr;
int getEnvStat = vm->GetEnv((void **)&env, JNI_VERSION_1_4);
if (getEnvStat == JNI_EDETACHED){
if (vm->AttachCurrentThread(&env, NULL) != 0){
MY_LOG_ERROR("AttachCurrentThread was not successful. "
"This may be due to the thread being attached already to another JVM instance.");
}
else{
attachCurrentThread = true;
}
}
else if (getEnvStat == JNI_OK){
//no need to attach, already attached
}
else if (getEnvStat == JNI_EVERSION){
MY_LOG_ERROR("GetEnv: version not supported");
}
return env;
};
void FreeEnv(JNIEnv * env) {
if (this->lockedEnv != nullptr) {
return;
}
if (attachCurrentThread) {
vm->DetachCurrentThread();
attachCurrentThread = false;
}
}
protected:
bool attachCurrentThread = false;
};
struct JNICallback : public SimpleJNIClass
{
jmethodID callbackMethod = nullptr;
JNICallback(JNIEnv *env, jobject classRef,
const std::string & methodName, const std::string & methodSignature)
: SimpleJNIClass(env, classRef) {
jclass callbackClass = (jclass)env->GetObjectClass(this->instanceRef);
this->callbackMethod = env->GetMethodID(callbackClass, methodName.c_str(), methodSignature.c_str());
};
virtual ~JNICallback() {
callbackMethod = nullptr;
}
template<typename... Args>
void RunVoid(Args... args) {
JNIEnv *env = this->GetEnv();
env->CallVoidMethod(this->instanceRef, this->callbackMethod, args...);
FreeEnv(env);
}
};
struct JNIClass : public SimpleJNIClass {
jclass callbackClass;
std::unordered_map<std::string, jmethodID> callbacks;
JNIClass(JNIEnv *env, jobject classRef)
: SimpleJNIClass(env, classRef) {
this->callbackClass = (jclass)env->NewGlobalRef((jclass)env->GetObjectClass(this->instanceRef));
};
JNIClass(JNIEnv *env, const std::string & classRefName)
: SimpleJNIClass(env) {
this->callbackClass = (jclass)env->NewGlobalRef(env->FindClass(classRefName.c_str()));
};
virtual ~JNIClass() {
if (JNIEnv * env = GetEnv()) {
env->DeleteGlobalRef(callbackClass);
callbackClass = nullptr;
FreeEnv(env);
}
callbacks.clear();
};
void AddMethod(const std::string & methodName, const std::string & methodSignature) {
JNIEnv *env = this->GetEnv();
this->callbacks[methodName] = env->GetMethodID(this->callbackClass, methodName.c_str(), methodSignature.c_str());
FreeEnv(env);
};
template<typename... Args>
void InitNewInstance(Args... args) {
JNIEnv *env = this->GetEnv();
this->SetInstance(env->NewObject(this->callbackClass, this->callbacks["<init>"], args...));
FreeEnv(env);
}
void SetInstance(jobject instRef) {
JNIEnv *env = this->GetEnv();
env->DeleteGlobalRef(this->instanceRef);
this->instanceRef = env->NewGlobalRef(instRef);
FreeEnv(env);
}
template<typename... Args>
void RunVoid(const std::string & name, Args... args) {
JNIEnv *env = this->GetEnv();
env->CallVoidMethod(this->instanceRef, this->callbacks[name], args...);
FreeEnv(env);
};
};
在JNI_OnLoad中,我设置了全局变量
JNIClass * fcDataClass
: fcDataClass = new JNIClass(env, "com/example/MyClass");
后来在Android的一些JNI回调中,我有:
int resCount = 3;
JNIEnv *lockedEnv = fcDataClass->LockEnv();
jobjectArray dataArray = lockedEnv->NewObjectArray(resCount, fcDataClass->callbackClass, NULL);
//do something with data
fcDataClass->UnLockEnv();
问题是,
NewObjectArray
使用以下命令使整个应用程序崩溃:SIGSEGV (signal SIGSEGV: invalid address (fault address: 0x9c))
使用调用栈:
art::JavaVMExt::JniAbort(char const*, char const*) 0x00000000ef74815c
art::JavaVMExt::JniAbortV(char const*, char const*, std::__va_list) 0x00000000ef7495ba
art::ScopedCheck::AbortF(char const*, ...) 0x00000000ef5d8eee
art::ScopedCheck::CheckThread(_JNIEnv*) 0x00000000ef5d8a0a
art::ScopedCheck::CheckPossibleHeapValue(art::ScopedObjectAccess&, char, art::JniValueType) 0x00000000ef5d7af2
art::ScopedCheck::Check(art::ScopedObjectAccess&, bool, char const*, art::JniValueType*) 0x00000000ef5d6fce
art::CheckJNI::NewObjectArray(_JNIEnv*, int, _jclass*, _jobject*) 0x00000000ef5d3808
_JNIEnv::NewObjectArray(int, _jclass*, _jobject*) jni.h:858
最佳答案
请参见CheckThread()源。如果未附加当前线程,或者尝试使用属于其他trhead的JNIEnv*
,它将中止。
您的fcDataClass->LockEnv()
似乎与线程无关。获取正确的JNIEnv*
的合法方法是调用AttachCurrentThread()
(see how WebRTC does this)。处置此类线程之前,请不要忘记使用DetachCurrentThread()
。