Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration with Flutter #3

Closed
Sitin opened this issue Dec 22, 2024 · 14 comments
Closed

Integration with Flutter #3

Sitin opened this issue Dec 22, 2024 · 14 comments

Comments

@Sitin
Copy link

Sitin commented Dec 22, 2024

Not sure it 100% belongs here. Nevertheless, let's give it a try.

I am trying to integrate your library with Flutter (I've tied both Rinf and Flutter Rust Bridge). But it fails to build:

SEVERE: ================================================================================
SEVERE: Cargokit BuildTool failed with error:
SEVERE: --------------------------------------------------------------------------------
SEVERE: External Command: rustup "run" "stable" "cargo" "build" "--manifest-path" "/Users/******/native/hub/Cargo.toml" "-p" "hub" "--target" "aarch64-linux-android" "--target-dir" "/Users/******/build/rinf/build"
SEVERE: Returned Exit Code: 101
SEVERE: --------------------------------------------------------------------------------
SEVERE: STDOUT:
SEVERE: --------------------------------------------------------------------------------
SEVERE: STDERR:
SEVERE: Compiling jni-min-helper v0.2.2
SEVERE: error: failed to run custom build command for `jni-min-helper v0.2.2`
SEVERE: 
SEVERE: Caused by:
SEVERE:   process didn't exit successfully: `/Users/******/build/rinf/build/debug/build/jni-min-helper-606745cc38b3b1ed/build-script-build` (exit status: 101)
SEVERE:   --- stdout
SEVERE:   cargo:rerun-if-changed=InvocHdl.java
SEVERE:   cargo:rerun-if-changed=BroadcastRec.java
SEVERE: 
SEVERE:   --- stderr
SEVERE:   thread 'main' panicked at /Users/******/.cargo/registry/src/index.crates.io-6f17d22bba15001f/android-build-0.1.0/src/env_paths/mod.rs:77:26:
SEVERE:   either ANDROID_JAR or [ANDROID_PLATFORM, ANDROID_API_LEVEL, ANDROID_SDK_VERSION] must be set
SEVERE:   note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
SEVERE: --------------------------------------------------------------------------------
SEVERE: #0      runCommand (package:build_tool/src/util.dart:118:5)
SEVERE: #1      RustBuilder.build (package:build_tool/src/builder.dart:139:5)
SEVERE: <asynchronous suspension>
SEVERE: #2      ArtifactProvider.getArtifacts (package:build_tool/src/artifacts_provider.dart:68:25)
SEVERE: <asynchronous suspension>
SEVERE: #3      BuildGradle.build (package:build_tool/src/build_gradle.dart:32:23)
SEVERE: <asynchronous suspension>
SEVERE: #4      BuildGradleCommand.runBuildCommand (package:build_tool/src/build_tool.dart:62:5)
SEVERE: <asynchronous suspension>
SEVERE: #5      BuildCommand.run (package:build_tool/src/build_tool.dart:34:5)
SEVERE: <asynchronous suspension>
SEVERE: #6      CommandRunner.runCommand (package:args/command_runner.dart:212:13)
SEVERE: <asynchronous suspension>
SEVERE: #7      runMain (package:build_tool/src/build_tool.dart:248:5)
SEVERE: <asynchronous suspension>
SEVERE: --------------------------------------------------------------------------------
SEVERE: BuildTool arguments: [build-gradle]
SEVERE: ================================================================================

FAILURE: Build failed with an exception.

All other Rust code successfully builts for Android.

I am also curious: is it possible to avoid using calls to Java classes? Sounds like nusb has everything we need to interoperate with USB interface.

@wuwbobo2021
Copy link
Owner

wuwbobo2021 commented Dec 22, 2024

The current workaround is to configure environment variables according to https://docs.rs/android-build/latest/android_build.

The error is from https://docs.rs/android-build/latest/src/android_build/env_paths/mod.rs.html#68-84. This should be considered as a bug of jni-min-helper build script, which should use the prebuilt dex file fallback instead of panicking if android-build isn't configured. Thank you for pointing out the problem, I will fix it soon.

"is it possible to avoid using calls to Java classes?" Since version 0.2.1, jni_min_helper::BroadcastReceiver is used to track the state of the permission request and to support the hotplug event stream.

"Sounds like nusb has everything we need to interoperate with USB interface." Currently it is not the case even with v0.2.0 (which shouldn't cause this build error). kevinmehall/nusb#86

@wuwbobo2021
Copy link
Owner

I've released jni-min-helper 0.2.3 which solves this problem. Feel free to report if you have encountered other problems.

@Sitin
Copy link
Author

Sitin commented Dec 23, 2024

It seems that I still have an issue but now of a different kind.

The library failes on probe().

match android_usbser::CdcSerial::probe() {
    Ok(devices) => {
        rinf::debug_print!("Devices (android_usbser): {:#?}", &devices);
    }
    Err(err) => {
        rinf::debug_print!("Error (android_usbser): {:#?}", &err);
    },
};

This gives me:

I/flutter (25084): Error (android_usbser): Custom {
I/flutter (25084):     kind: Other,
I/flutter (25084):     error: NullPtr(
I/flutter (25084):         "call_method obj argument",
I/flutter (25084):     ),
I/flutter (25084): }

Just in case. I had an error before related to the uninitialized Android context that I've fixed in a way described in flutter_rust_bridge/issues/1323.

@Sitin
Copy link
Author

Sitin commented Dec 23, 2024

I suspect that this is related to the way how context is initialized in Flutter. It seems that we need to cache classes during JVM onLoad as described here.

@wuwbobo2021 wuwbobo2021 reopened this Dec 23, 2024
@wuwbobo2021
Copy link
Owner

The error message came from https://docs.rs/jni/0.21.1/src/jni/wrapper/jnienv.rs.html#1370. Since it's got in CdcSerial::probe, I found a possible route that leads to this error:

I guess it's unlikely that the error came from some other location.

Please add jni-min-helper = "0.2.3" in the Cargo.toml of your Rust crate, and try jni-min-helper::android_context() before CdcSerial::probe in your code, check if the returned JNI reference of the context is null. jni-min-helper::JObjectGet can be introduced for a convenient is_null() method.

Self-note: I found another possible improvement for jni-min-helper: https://docs.rs/jni-min-helper/0.2.3/src/jni_min_helper/lib.rs.html#159 prints the Rust side backtrace only when Java exceptions occurred, it's not printed for other errors from jni-rs (like NullPtr).

@Sitin
Copy link
Author

Sitin commented Dec 23, 2024

Thank you for reopening the issue. Here is what I have for:

let ctx = jni_min_helper::android_context();
rinf::debug_print!("Android context: {:?}", ctx);

match android_usbser::CdcSerial::probe() {
    Ok(devices) => {
        rinf::debug_print!("Devices (android_usbser): {:#?}", &devices);
    }
    Err(err) => {
        rinf::debug_print!("Error (android_usbser): {:#?}", &err);
    },
};

Result:

let ctx = jni_min_helper::android_context();
    rinf::debug_print!("Android context: {:?}", ctx);
    
    match android_usbser::CdcSerial::probe() {
        Ok(devices) => {
            rinf::debug_print!("Devices (android_usbser): {:#?}", &devices);
        }
        Err(err) => {
            rinf::debug_print!("Error (android_usbser): {:#?}", &err);
        },
    };

I must add that I initialize android context in the way descibed here (method 1). Without such initialization Android context is not present.

@Sitin
Copy link
Author

Sitin commented Dec 23, 2024

I've also tried the same with Flutter Rust Bridge in case that Rinf has issues. Still the same (but they have a better logging out of the box):

W/my_rust_lib::api..( 2368): Android context: JObject { internal: 0x0, lifetime: PhantomData<&()> }
V/jni::wrapper::java_vm..( 2368): calling unchecked JavaVM method: GetEnv
V/jni::wrapper::java_vm..( 2368): looking up JavaVM method GetEnv
V/jni::wrapper::java_vm..( 2368): found JavaVM method
V/jni::wrapper::jnienv( 2368): calling checked jni method: NewStringUTF
V/jni::wrapper::jnienv( 2368): looking up jni method NewStringUTF
V/jni::wrapper::jnienv( 2368): found jni method
V/jni::wrapper::jnienv( 2368): checking for exception
V/jni::wrapper::jnienv( 2368): calling unchecked jni method: ExceptionCheck
V/jni::wrapper::jnienv( 2368): looking up jni method ExceptionCheck
V/jni::wrapper::jnienv( 2368): found jni method
V/jni::wrapper::jnienv( 2368): no exception found
V/jni::wrapper::jnienv( 2368): calling unchecked jni method: DeleteLocalRef
V/jni::wrapper::jnienv( 2368): looking up jni method DeleteLocalRef
V/jni::wrapper::jnienv( 2368): found jni method
E/my_rust_lib::api..( 2368): Error (android_usbser): Custom {
E/my_rust_lib::api..( 2368):     kind: Other,
E/my_rust_lib::api..( 2368):     error: NullPtr(
E/my_rust_lib::api..( 2368):         "call_method obj argument",
E/my_rust_lib::api..( 2368):     ),
E/my_rust_lib::api..( 2368): }

@wuwbobo2021
Copy link
Owner

The problem is shown: AndroidContext::context() got from ndk_context::android_context() is null!

Looking into https://cjycode.com/flutter_rust_bridge/guides/how-to/ndk-init#method-1, I don't know what's passed to the second argument of JNI_OnLoad in your case, which should be the reference of Android context object (not to be confused with the Rust AndroidContext object).

I think the JVM pointer got from AndroidContext is non-null (the first argument of JNI_OnLoad). With the second argument, just make clear of when JNI_OnLoad is called: is the Android context available when loading the native library? Try print debug information of the res argument.

I haven't learnt Flutter previously, and I'm afraid that I can't help with the issue any further. The issue may still be closed because it's not a bug of my crate.

@Sitin
Copy link
Author

Sitin commented Dec 23, 2024

It seems that this is indeed a Flutter issue. I've created an issue in Flutter Rust Bridge.

@wuwbobo2021
Copy link
Owner

wuwbobo2021 commented Dec 24, 2024

jni-min-helper is fixed, adding a workaround for this issue. However, check_attached_intent() will not work in this case.

@Sitin
Copy link
Author

Sitin commented Dec 24, 2024

Thank you a lot, @wuwbobo2021! It works on Flutter with FRB with your fixes. I've performed a dirty test with my device, written a command and receive an output. This means that this issue is resolved.

@Sitin
Copy link
Author

Sitin commented Dec 24, 2024

BTW, how the workaround for this issue works?

@wuwbobo2021
Copy link
Owner

wuwbobo2021 commented Dec 24, 2024 via email

@Sitin
Copy link
Author

Sitin commented Dec 25, 2024

I see. You mean this. Thank you a lot again for the quick fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants