rustjava-native-interfaceandroid-webviewandroid-loopernative-activity

Creating Android WebView in NativeActivity with JNI in rust


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.


Solution

  • 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: