I have a general help question about targeting Android.
I've seen a few comments in this forum but they are often several years old or refer to crates that are no longer maintained.
So, if you are going to build some logic in Rust that you want to run on Android (let's leave UI out of it for simplicity's sake), what would you do? Is it just a matter of adjusting your target architecture? Do you generally output a .so and link it in using JNI?
Yes. You output a shared library that you use with JNI.
Rustup will allow you to add the common Android targets, but you'll still need to use a linker that works with Android. When rust asks for a linker, it really wants gcc. On Linux there's generally no problem, but on MacOS the default build system is clang and it doesn't work. The Android build system includes a version of gcc you can use on MacOS as a rust linker, but it isn't the default in the latest NDK.
Thanks, but for my use cases I'm looking for more like @fstephany's suggestion than a swig-based bindgen type solution. I want to target the Android device directly as opposed to FFI.
Pretty much everything is using some JNI. The question is really how thick that layer is, and whether you have to think about it. Both 'cargo apk' and the android SDL2 port use JNI with a thin Java wrapper they've written. They pump through events and get a graphics context you can draw on from native code.
Yeah, I figured that. I have no problem bundling my public API as a shared lib and just calling that from JNI as needed on my devices. Thanks, all for the advice!
One thing I'll add from recent experience (although grain of salt, our app is stuck on ndk r10e for the time being) -- backtrace-sys, openssl-sys, and a few other crates were pretty gnarly to convince to build using the old NDK toolchains -- I'm assuming it's easier on newer ones with CMake, but passing all of the correct configuration wasn't very straightforward in my case. I based our integration heavily on Lliwynd: Rust for Android games using SDL2, with some changes.
Overall, though, there are a few things I learned that might prove useful to others:
CARGO_TARGET_I686_LINUX_ANDROID_LINKER=... is much more version-control-friendly than .cargo/config values, at least for us (we have scripts to populate the correct env vars with absolute paths per dev machine, rather than everyone having to do it themselves)
piggybacking on the android cross-compilation configuration was the only reliable way I found for being able to correctly configure & build a number of crates which build their own C or C++ code
NDK libs and host-machine unit tests (think robolectric) are a bit rough. I used conditional compilation to separate out the JNI interface from Android-specific interfaces (logging right now) so that I can compile for the host OS and still include the JNI lib in unit tests
I am going to update our NDK soon, come hell or high water
I was planning to write a blog post whenever this actually ships, but I saw this here and thought a quick brain-dump might be helpful to someone passing through.
We have a slightly quirky monorepo setup, so some of the paths might be confusing, but here's a gist with the Android.mk and gradle changes I've used to make it work for our builds.
@anp The gist link is broken, any chance you could update it?
We're doing things our own way in aw-android, which builds aw-server-rust as a shared lib and calls it from JNI. Works well, although our build process is a bit messy.
I've also tried getting Mozilla's rust-android-gradle to work, but for some reason some deps (openssl-sys) don't play nice with it, which is weird since I expect the process to be about the same.