dioxus icon indicating copy to clipboard operation
dioxus copied to clipboard

Fix Mobile Tutorial For Android

Open wakearray opened this issue 2 years ago • 3 comments

Problem

The mobile tutorial is incorrect and following it currently will result in an app that loads the tauri website.

Steps To Reproduce

Steps to reproduce the behavior:

  • Follow Android steps in the tutorial found at https://dioxuslabs.com/learn/0.4/getting_started/mobile

Expected behavior

The app to both render on the device and the buttons to add additional items to the list.

Environment:

  • Dioxus version: v0.4.3
  • Rust version: 1.74.1
  • OS info: Windows 11
  • App platform: Android
  • Tested device: Cubot P80 running AOSP Android 13

Questionnaire

  • [ ] I'm interested in fixing this myself but don't know where to start
  • [ ] I would like to fix and I have a solution
  • [ ] I don't have time to fix this right now, but maybe later

Additional Information After following the instructions in the tutorial, I had a Windows app and an Android app that loaded the tauri website.

As I'm new to Rust and haven't made an Android app in around 10 years, it took me awhile to figure out that the lib.rs was the only thing being included in the Android build, where the main function was building a wry webview pointed at tauri. That's the current boilerplate for wry. Doing cargo build told me there were too many options and to pick either dioxus-mobile-test or --bin dioxus-mobile-test-desktop so running the one labeled desktop (because I wanted to test the Windows app as well and didn't see instructions in the mobile documentation for also running a Windows build), I ended up running the desktop.rs that only referenced the lib.rs's main function.

After a couple of days I figure out what I'm doing wrong and put the example code into the main function of the lib.rs file. Works on Windows now, but on Android it loads the page and soft crashes when you click on any buttons. The soft crash is very strange, if you use chrome remote inspect tools to inspect the app page before you click any buttons, you can see the URL, the page as it's rendered on the phone letting you emulate touches or mouse clicks, the DOM elements, and console telling you a favicon wasn't found. However, if you click a button before you inspect it, the URL on the webviews (I'm still not sure why there's two of them) will disappear and inspecting them won't show you anything. Just a grey empty inspect window with nothing in it. I used logcat and debugging messages to see if I could locate the issue and logcat shows the error [AUX]GuiExtAuxCheckAuxPath:670: Null anb on launching the app, as well as twice after clicking a button.

Code from lib.rs
use anyhow::Result;
use dioxus::prelude::*;
use log::*;
#[cfg(target_os = "android")]
use wry::android_binding;

#[cfg(target_os = "android")]
fn init_logging() {
    android_logger::init_once(
        android_logger::Config::default()
            .with_min_level(log::Level::Trace)
            .with_tag("dioxus-mobile-test"),
    );
}

#[cfg(not(target_os = "android"))]
fn init_logging() {
    env_logger::init();
}

#[cfg(any(target_os = "android", target_os = "ios"))]
fn stop_unwind<F: FnOnce() -> T, T>(f: F) -> T {
    match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
        Ok(t) => t,
        Err(err) => {
            error!("attempt to unwind out of `rust` with err: {:?}", err);
            std::process::abort()
        }
    }
}

#[cfg(any(target_os = "android", target_os = "ios"))]
fn _start_app() {
    debug!("_start_app()");
    stop_unwind(|| main().unwrap());
}

#[no_mangle]
#[inline(never)]
#[cfg(any(target_os = "android", target_os = "ios"))]
pub extern "C" fn start_app() {
    #[cfg(target_os = "android")]
    android_binding!(com_example, dioxus-mobile-test, _start_app);
    #[cfg(target_os = "ios")]
    _start_app()
}

pub fn main() -> Result<()> {
    init_logging();
    dioxus_mobile::launch(app);
    Ok(())
}

fn app(cx: Scope) -> Element {
    let items = cx.use_hook(|| vec![1, 2, 3]);

    debug!("Hello from the app");

    render! {
        div {
            h1 { "Hello, Mobile"}
            div { margin_left: "auto", margin_right: "auto", width: "200px", padding: "10px", border: "1px solid black",
                button {
                    onclick: move|_| {
                        println!("Clicked!");
                        items.push(items.len()+1);
                        println!("Item list is now longer");
                        cx.needs_update_any(ScopeId(0));
                        println!("Requested update");
                    },
                    "Add item"
                }
                for item in items.iter() {
                    div { "- {item}" }
                }
            }
        }
    }
}

Notes about the code above

  • The use of dioxus_mobile::launch(app); vs the dioxus_desktop::launch_cfg(app, ...); used in the tutorial did not affect the outcome in any way. The app still ran the same in Windows and still crashed the same on Android.
  • The init_logging() is included lib.rs boilerplate code that I thought might be helpful. For me it wasn't.
  • start_app() and stop_unwind() are also included boilerplate from lib.rs and the app would only full crash without them. Wouldn't start a single activity.

I recommend adding start_app() and stop_unwind to the mobile tutorial if Android becomes a priority in the future. use wry::android_binding; was necessary for those to work if I remember correctly.

When running that code, Accessing hidden method Landroid/view/MotionEvent;->getEventTimeNano()J (unsupported, reflection, allowed) occurs first, then println!("Clicked!");, then finally println!("Item list is now longer"); is triggered and then the 2x Null anb errors happen immediately after and end with the soft crash. So it seems that cx.needs_update_any(ScopeId(0)); is what's triggering the Null anb errors.

After looking around it seems like getEventTimeNano() has been replaced with getEventTimeNanos(), but I have no idea what's calling it. After googling around it doesn't seem like it really matters since it's greylisted (though I think it's supposed to be blacklisted for Android 15) and is likely outside the scope of Dioxus.

I was able to find several comments online about [AUX]GuiExtAuxCheckAuxPath:663: Null anb but none about [AUX]GuiExtAuxCheckAuxPath:670: Null anb Some of what I saw about 663 suggested it had to do with cleartext webview (which would make sense in this case), with the advice being to add https URLs rather than http. Since I couldn't figure out what was causing 670 and I was unable to determine how to make Dioxus output https, I decided to try different code in the app function to rule out other potential issues.

The High-Five Counter example code:
let mut count = use_state(cx, || 0);

    debug!("Hello from the app");

    cx.render(rsx!(
        h1 { "High-Five counter: {count}" }
        button {
            onclick: move |_| {
                // changing the count will cause the component to re-render
                println!("Test 1");
                count += 1;
                println!("Test 2");
            },
            "Up high!"
        }
        button {
            onclick: move |_| {
                // changing the count will cause the component to re-render
                println!("Test 3");
                count -= 1;
                println!("Test 4");
            },
            "Down low!"
        }
    ))

In this case I got the same Null anb error at app launch, then twice again after pressing the "Up high!" button right after the println!("Test 1"); was triggered. When pressing the "Down low!" button after pressing the "Up high!" button I was able to log one additional Null anb error, though no additional println!() macros fired. On additional tests I was only able to click one button (either one, didn't matter) and get the two Null anb errors and refused to register additional touches.

Everything still runs on Windows fine, but Android is just not working.

If the mobile tutorial is working in the future, I highly recommend making an official YouTube video including every step of getting it setup for Android specifically (a separate video for each platform would really be ideal). That would go a long way to on-boarding new and inexperienced users.

wakearray avatar Dec 30 '23 05:12 wakearray

  • The use of dioxus_mobile::launch(app); vs the dioxus_desktop::launch_cfg(app, ...); used in the tutorial did not affect the outcome in any way. The app still ran the same in Windows and still crashed the same on Android.
  • The init_logging() is included lib.rs boilerplate code that I thought might be helpful. For me it wasn't.
  • start_app() and stop_unwind() are also included boilerplate from lib.rs and the app would only full crash without them. Wouldn't start a single activity.

I recommend adding start_app() and stop_unwind to the mobile tutorial if Android becomes a priority in the future. use wry::android_binding; was necessary for those to work if I remember correctly.

That code is generated by cargo-mobile2 and shouldn't need to be modified for dioxus mobile.

When running that code, Accessing hidden method Landroid/view/MotionEvent;->getEventTimeNano()J (unsupported, reflection, allowed) occurs first, then println!("Clicked!");, then finally println!("Item list is now longer"); is triggered and then the 2x Null anb errors happen immediately after and end with the soft crash. So it seems that cx.needs_update_any(ScopeId(0)); is what's triggering the Null anb errors.

After looking around it seems like getEventTimeNano() has been replaced with getEventTimeNanos(), but I have no idea what's calling it. After googling around it doesn't seem like it really matters since it's greylisted (though I think it's supposed to be blacklisted for Android 15) and is likely outside the scope of Dioxus.

I was able to find several comments online about [AUX]GuiExtAuxCheckAuxPath:663: Null anb but none about [AUX]GuiExtAuxCheckAuxPath:670: Null anb Some of what I saw about 663 suggested it had to do with cleartext webview (which would make sense in this case), with the advice being to add https URLs rather than http. Since I couldn't figure out what was causing 670 and I was unable to determine how to make Dioxus output https, I decided to try different code in the app function to rule out other potential issues.

The High-Five Counter example code:

let mut count = use_state(cx, || 0);

    debug!("Hello from the app");

    cx.render(rsx!(
        h1 { "High-Five counter: {count}" }
        button {
            onclick: move |_| {
                // changing the count will cause the component to re-render
                println!("Test 1");
                count += 1;
                println!("Test 2");
            },
            "Up high!"
        }
        button {
            onclick: move |_| {
                // changing the count will cause the component to re-render
                println!("Test 3");
                count -= 1;
                println!("Test 4");
            },
            "Down low!"
        }
    ))

In this case I got the same Null anb error at app launch, then twice again after pressing the "Up high!" button right after the println!("Test 1"); was triggered. When pressing the "Down low!" button after pressing the "Up high!" button I was able to log one additional Null anb error, though no additional println!() macros fired. On additional tests I was only able to click one button (either one, didn't matter) and get the two Null anb errors and refused to register additional touches.

Everything still runs on Windows fine, but Android is just not working.

If the mobile tutorial is working in the future, I highly recommend making an official YouTube video including every step of getting it setup for Android specifically (a separate video for each platform would really be ideal). That would go a long way to on-boarding new and inexperienced users.

The soft crash is caused by https://github.com/DioxusLabs/dioxus/issues/1158

https://github.com/DioxusLabs/docsite/pull/191 links to a fix for the android soft crash, makes it more clear what your lib.rs should look like and removes the extra main.rs file

ealmloff avatar Dec 31 '23 23:12 ealmloff

Thanks for the quick reply and the quick fix! I just tested out the new code and it works great, though I'd recommend a modification on line 253 from items.write().push(0); which only outputs 0's in the list, to something like:

let len = items.read().len();
items.write().push(len+1);

wakearray avatar Jan 01 '24 06:01 wakearray

The example crashes for me (Android 8.1), after fixing the Result syntax error, removing the main.rs function and trying wry 0.28.3 (higher versions are not compatible).

No implementation found for void com.example.example_dioxus.WryActivity.create(com.example.example_dioxus.WryActivity) (tried Java_com_example_example_1dioxus_WryActivity_create and Java_com_example_example_1dioxus_WryActivity_create__Lcom_example_example_1dioxus_WryActivity_2)
--------- beginning of crash
2024-01-08 00:09:09.781 25799-25799 AndroidRuntime          com.example.example_dioxus           D  Shutting down VM
2024-01-08 00:09:09.783 25799-25799 AndroidRuntime          com.example.example_dioxus           E  FATAL EXCEPTION: main
                                                                                                    Process: com.example.example_dioxus, PID: 25799
                                                                                                    java.lang.UnsatisfiedLinkError: No implementation found for void com.example.example_dioxus.WryActivity.create(com.example.example_dioxus.WryActivity) (tried Java_com_example_example_1dioxus_WryActivity_create and Java_com_example_example_1dioxus_WryActivity_create__Lcom_example_example_1dioxus_WryActivity_2)
                                                                                                    	at com.example.example_dioxus.WryActivity.create(Native Method)
                                                                                                    	at com.example.example_dioxus.WryActivity.onCreate(WryActivity.kt:59)
                                                                                                    	at android.app.Activity.performCreate(Activity.java:7009)
                                                                                                    	at android.app.Activity.performCreate(Activity.java:7000)
                                                                                                    	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
                                                                                                    	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2772)
                                                                                                    	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2897)
                                                                                                    	at android.app.ActivityThread.-wrap11(Unknown Source:0)
                                                                                                    	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1623)
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:106)
                                                                                                    	at android.os.Looper.loop(Looper.java:164)
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:6548)
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:445)
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:857)

The original Tauri webpage version was working.

I also get this every time I try cargo android run; I have to manually uninstall it:

adb: failed to install /home/frank/repos/example-dioxus/gen/android/app/build/outputs/apk/arm64/debug/app-arm64-debug.apk: Failure [INSTALL_FAILED_ALREADY_EXISTS: Attempt to re-install com.example.example_dioxus without first uninstalling.]

I also noticed that the image of the code on the example page (under iOS) doesn't correspond with the code given.

I shouldn't use dioxus in production yet, should I?

fr-an-k avatar Jan 07 '24 23:01 fr-an-k

Should be updated, tested on m1 that android works. That being said, the rust tooling for mobile is quite raw and will get a lot more focus in the next two releases.

jkelleyrtp avatar Mar 27 '24 03:03 jkelleyrtp