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.
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()
.