Reputation: 735
I'm trying to connect a android.webkit.WebView object to a NativeActivity, but I'm failing simply trying to create the WebView object. That code looks like this
#[cfg(target_os = "android")]
pub fn setup(ctx: AndroidContext, env: AttachGuard) -> JObject {
let class = env.find_class("android/webkit/WebView").unwrap();
let context: *mut _jobject = ctx.context().cast();
let webview = env
.new_object(
class,
"(Landroid/content/Context;)V",
&[JValue::Object(context.into())],
)
.unwrap();
env.call_method(
context,
"setContentView",
"(Landroid/view/View;)V",
&[webview.into()],
)
.unwrap()
.v()
.unwrap();
...
}
When I try to create the WebView I see this crash with logcat
06-26 23:17:30.471 30695 30727 E AndroidRuntime: FATAL EXCEPTION: Thread-2
06-26 23:17:30.471 30695 30727 E AndroidRuntime: Process: rust.example.android, PID: 30695
06-26 23:17:30.471 30695 30727 E AndroidRuntime: java.lang.RuntimeException: WebView cannot be initialized on a thread that has no Looper.
06-26 23:17:30.471 30695 30727 E AndroidRuntime: at android.webkit.WebView.<init>(WebView.java:670)
06-26 23:17:30.471 30695 30727 E AndroidRuntime: at android.webkit.WebView.<init>(WebView.java:604)
06-26 23:17:30.471 30695 30727 E AndroidRuntime: at android.webkit.WebView.<init>(WebView.java:587)
06-26 23:17:30.471 30695 30727 E AndroidRuntime: at android.webkit.WebView.<init>(WebView.java:574)
06-26 23:17:30.471 30695 30727 E AndroidRuntime: at android.webkit.WebView.<init>(WebView.java:564)
It appears that from the context on the rust side there is a looper (based on getting the ThreadLooper via a call to for_thread and checking that it's not storing a null pointer). However it does appear that there isn't one from the JVM perspective since attempts to log the results of a call to myLooper on the android.os.Looper object via JNI yields a null looper. I suppose this makes sense since the UI loop was not setup the way it normally would be with a Java / Kotlin Android app. The question is, is there a way around this?
Can I execute the JNI calls within a context that's part of the the non-null looper, or share that looper with the JNI context, or create a looper via JNI for the context within the JVM. Since this is supposed to be part of a rust library I'm try to avoid needing to add any java boiler plate. Is there a way to avoid that? I'm not exactly an Android or JNI expert so I'm pretty far out over my skis with this project. Any help is appreciated.
Upvotes: 1
Views: 734
Reputation: 31055
By far the simplest solution is simply to launch a new (non-native) activity for the duration of the web interaction.
If you cannot do that, you will have to do (at least) the following:
Looper.prepare()
method to associate your thread with a Looperquit()
is called on the Looper instanceLooper.loop()
.Upvotes: 3