lukax 7 hours ago

Do not write the bindings manually. Just use the amazing uniffi-rs library from Mozilla.

https://github.com/mozilla/uniffi-rs

You can generate bindings for multiple languages. It supports error handling on both sides and latest versions also support native async integration.

I've used it to reuse the same Rust engine in iOS and Android apps and write native UI.

https://github.com/koofr/vault

  • 4hg4ufxhy an hour ago

    The bindings are inefficient doing excessive cloning. But if performance is not a concern then its fine.

  • cadamsdotcom 7 hours ago

    Thanks! Anywhere you hit issues?

    • lukax an hour ago

      Not really. But I didn't use async (was not supported yet when I started using it).

      Bindings were easy, everything else (building, linking,...) was a bit pain to setup because there no good examples.

koakuma-chan 9 minutes ago

I found mixing Bun and Rust works pretty well. Bun has gotten many cool new things recently which feel great to use and it has a nice FFI API. So I have Next.js apps running in Bun runtime and anything CPU bound is written in Rust and called via FFI.

never_inline 4 hours ago

1. The article seems kind-of shallow. I didn't see any concrete (qualitative or quantitative) remarks about the "fast" part. I don't doubt you have reasons to do this - but I expected some information on what component are you writing using Rust + JNI, and how it helped? Or is it just a demo?

At some point, repeated calls into the JNI are counter-productive to performance, since the JIT can not optimize them. Pinning affecting garbage collection is another potential drawback if any of your rust calls are long lived. If we don't measure and just conclude "we are fast because rust is faster than Java, and we took average of both speeds", it's a disservice.

2. Also, I see unsafe for each call? I'd rather isolate this into a class / different file, since in JNI only few types of calls are possible. (method returning one of the primtive types, an object or `void`). This is the approach I took in dart jnigen. (Though there, the call is Dart -> Java, not Java -> Native language).

    unsafe {
      env.call_method_unchecked(
                java_logger,
                logger_method,
                ReturnType::Primitive(Primitive::Void),
                &[JValue::from(format_msg(record)).as_jni()]
      );
    }
3. I believe some details are missing here. What's native_add_one mapped to? And how is tokio futures awaited from Java? I believe that's the important part you should be presenting.

    public CompletableFuture<Integer> add_one(int x) {
        long futureId = native_add_one(x); // Call Rust
        return AsyncRegistry.take(futureId); // Get CompletableFuture
    }
4. Also please don't use ChatGPT for writing anything. It totally derails the reader by mentioning irrelevant details and long winded corporate conclusion at the end of every sentence.
  • killme2008 4 hours ago

    This article summarizes our experience from a commercial project that runs on an in-vehicle Android system. In this project, we needed to invoke Rust code(DB) from Java(App), so we couldn't directly use the project’s source code for demonstration. Instead, we created a demo project: https://github.com/GreptimeTeam/rust-java-demo

    1. I agree that using Rust doesn't necessarily mean faster performance; it simply gives you the opportunity to implement some compute-intensive modules in Rust, which is a possible approach.

    2. This is a great suggestion, and we organized our project in the same way. You don’t need to use unsafe for every call. However, if you want to call JNI APIs from Rust, unsafe is required.

    3. Sorry, some details were missing here. We use AsyncRegistry(Java) as an intermediary. Before initiating an async operation in Rust, we need to call Java code in advance to register a future and obtain a unique future ID. After the async execution completes, we retrieve the registered future by its ID, and then complete it or complete it exceptionally depending on the async result. You can refer to this code: https://github.com/GreptimeTeam/rust-java-demo/blob/90ffa0ba... and https://github.com/GreptimeTeam/rust-java-demo/blob/90ffa0ba...

    4. This article was not generated by AI; it’s just that our official blog has a fixed template at the end. Sorry for the inconvenience.

aeonik 36 minutes ago

I thought JNI was being deprecated in favor of the new FFM interface.

https://openjdk.org/jeps/472

  • cogman10 20 minutes ago

    Notice the delivery version (24). That was literally just sent out in March. The FFM interface was stabilized in 22. 21 is the latest LTS version which is the furthest most orgs will go.

    • da_chicken 13 minutes ago

      That doesn't mean you should happily go out and build a new cathedral of technical debt.

  • immibis 8 minutes ago

    This says:

    > It is not a goal to deprecate JNI or to remove JNI from the Java Platform.

    It says they want to put a safety barrier in front of JNI, so you'll have to explicitly indicate that you want a module to use JNI.

bullen 4 hours ago

I agree that Java + native is the way to go.

But does rust really give you an edge over C/C++?

Here is how you do JNI with C++: http://move.rupy.se/file/jvm.txt

So simple it's ridiculous!

Then you can use RegisterNatives to give C++ API to the Java side instead of the stub (Java calls C++ .dll/.so) thing...

dbacar 2 hours ago

WHich part of the Java app was slow for you? And did you check on FFI (foreign function interface) ?

invalidname 7 hours ago

Why didn't OP use Panama or the modern Java native APIs which are far better than JNI?

  • cyberax 6 hours ago

    Android?

    • pjmlp 5 hours ago

      Proving the point about Android being Google's J++, and Kotlin their C#.