如何解决忽略从 C++ 调用 Java 方法
我试图在我的程序生命周期中多次执行的事件处理程序中从 C++ 中重复调用 Java 方法。
为此,我有以下代码。
- 一个 JNI 帮助程序类,用于获取 JNIEnv 的有效实例并对其进行适当清理。
#pragma once
#include <jni.h>
static bool getJniEnv(JavaVM *vm,JNIEnv **env) {
bool didAttachThread = false;
*env = nullptr;
auto get_env_result = vm->GetEnv((void**)env,JNI_VERSION_1_6);
if (get_env_result == JNI_EDETACHED) {
if (vm->AttachCurrentThread(env,NULL) == JNI_OK) {
didAttachThread = true;
} else {
throw;
}
} else if (get_env_result == JNI_EVERSION) {
// Unsupported JNI version.
throw;
}
return didAttachThread;
}
class ScopedEnv {
public:
ScopedEnv(JavaVM *vm) : vm(vm),attachedToVm(false) {
attachedToVm = getJniEnv(vm,&env);
}
ScopedEnv(const ScopedEnv&) = delete;
ScopedEnv& operator=(const ScopedEnv&) = delete;
virtual ~ScopedEnv() {
if (attachedToVm) {
vm->DetachCurrentThread();
attachedToVm = false;
}
}
JNIEnv *getEnv() const { return env; }
private:
bool attachedToVm;
JavaVM *vm;
JNIEnv *env;
};
- 一个 C++ 包装器,用于存储对实现我想要调用的方法的 Java 对象的全局引用:
class PageEventObserver {
JavaVM *vm;
jclass pageClass;
jobject pageObj;
public:
PageEventObserver(JavaVM *vm,jclass klass,jobject obj) : vm(vm),pageClass(klass),pageObj(obj) {}
~PageEventObserver();
void onLoadChanged(WebKitLoadEvent);
[...]
};
及其相应的实现:
[...]
void PageEventObserver::onLoadChanged(WebKitLoadEvent loadEvent) {
ALOGV("PageEventObserver::onLoadChanged tid %d",gettid());
try {
JNIEnv *env = ScopedEnv(vm).getEnv();
jmethodID onLoadChanged = env->GetMethodID(pageClass,"onLoadChanged","(I)V");
if (onLoadChanged == nullptr) {
throw;
}
ALOGV("Calling method env %p pageClass %p pageObj %p",env,pageClass,pageObj);
env->CallVoidMethod(pageObj,onLoadChanged,(int)loadEvent);
ALOGV("Called");
} catch(int) {
ALOGE("Could not send onLoadChanged event");
}
}
-
PageEventObserver
包装器的创建发生在 JNI 层对以下方法的初始调用时:
JNIEXPORT void JNICALL
Java_com_wpe_wpe_BrowserGlue_newWebView(JNIEnv* env,jobject,jobject pageObj,jint width,jint height)
{
ALOGV("BrowserGlue.newWebView tid %d",gettid());
jclass pageClass = env->GetObjectClass(pageObj);
jclass _pageClass = reinterpret_cast<jclass>(env->NewGlobalRef(pageClass));
jobject _pageObj = reinterpret_cast<jobject>(env->NewGlobalRef(pageObj));
JavaVM *vm;
env->GetJavaVM(&vm);
std::unique_ptr<PageEventObserver> observer = std::make_unique<PageEventObserver>(vm,_pageClass,_pageObj);
wpe_browser_glue_new_web_view(width,height,std::move(observer),[env,pageObj,pageClass] (long viewRef) {
jmethodID onReady = env->GetMethodID(pageClass,"onWebViewReady","(J)V");
if (onReady == nullptr) {
return;
}
ALOGV("webview %ld",(jlong)viewRef);
env->CallVoidMethod(pageObj,onReady,(jlong)viewRef);
});
}
在那里我创建了对 pageClass
和 pageObj
的全局引用(我怀疑我可以跳过创建对 pageClass
的全局引用,但这超出了问题的范围)和创建 PageEventObserver
实例,将全局引用传递给其构造函数。然后将 PageEventObserver
实例传递给 wpe_browser_glue_new_web_view
,最终将其保存为静态。
- 在某些时候,会执行一个事件处理程序:
static void onLoadChanged(WebKitWebView*,WebKitLoadEvent loadEvent,gpointer) {
ALOGV("onLoadCHanged %d",loadEvent);
pageObserver->onLoadChanged(loadEvent);
}
在此事件处理程序中,我使用静态 PageEventObserver
实例,调用其 onLoadChanged
方法。
第一次 执行此事件处理程序时,一切正常。我在日志中看到了这一点:
03-26 10:15:41.754 V [31342/31401] WPE Glue onLoadCHanged 0
03-26 10:15:41.754 V [31342/31401] WPE Glue PageEventObserver::onLoadChanged tid 31401
03-26 10:15:41.754 V [31342/31401] WPE Glue Calling method env 0xb400007915a86030 pageClass 0x294a pageObj 0x2956
03-26 10:15:41.754 V [31342/31401] WPE page0 onLoadChanged ...com.wpe.wpeview.WPEView{d68f3ce V.E...... ........ 0,154-1080,2151 #7f08019e app:id/wpe_view}
03-26 10:15:41.754 V [31342/31401] WPEView Load changed ///// <-- This is inside the Java method I want to call
03-26 10:15:41.754 V [31342/31401] WPE Glue Called
但是,在同一事件处理程序的后续执行中,Java 方法没有被调用,但我根本没有看到任何错误或崩溃:
03-26 10:15:45.807 V [31342/31401] WPE Glue onLoadCHanged 3
03-26 10:15:45.808 V [31342/31401] WPE Glue PageEventObserver::onLoadChanged tid 31401
03-26 10:15:45.808 V [31342/31401] WPE Glue Calling method env 0xb400007915a86030 pageClass 0x294a pageObj 0x2956
03-26 10:15:45.808 V [31342/31401] WPE Glue Called
注意 WPE Glue Calling ...
和 WPE Glue called
之间丢失的日志。
解决方法
我找到了罪魁祸首。关联的事件处理程序抛出了一个未捕获的异常:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
修复使一切按预期工作。我现在看到丢失的日志了。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。