[英]Ignored call to Java method from C++
我試圖在一個事件處理程序中反復調用 Java 方法,該方法在我的程序生命周期內執行多次。
為此,我有以下代碼。
#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;
};
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
的全局引用,但這不在問題的 scope 范圍內)並創建PageEventObserver
實例,將全局引用傳遞給其構造函數。 然后將PageEventObserver
實例傳遞給wpe_browser_glue_new_web_view
,最終將其保存為 static。
static void onLoadChanged(WebKitWebView*, WebKitLoadEvent loadEvent, gpointer) {
ALOGV("onLoadCHanged %d", loadEvent);
pageObserver->onLoadChanged(loadEvent);
}
在這個事件處理程序中,我使用了 static 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.
修復使一切按預期工作。 我現在看到丟失的日志。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.