Building and using a locally-modified version of JNA
Java Native Access is an important dependency for the Application Services components on Android, as it provides the low-level interface from the JVM into the natively-compiled Rust code.
If you need to work with a locally-modified version of JNA (e.g. to investigate an apparent JNA bug) then you may find these notes helpful.
The JNA docs do have an Android Development Environment guide that is a good starting point, but the instructions did not work for me and appear a little out of date. Here are the steps that worked for me:
-
Modify your environment to specify
$NDK_PLATFORM
, and to ensure the Android NDK tools for each target platform are in your$PATH
. On my Mac with Android Studio the config was as follows:export NDK_ROOT="$HOME/Library/Android/sdk/ndk/27.2.12479018" export NDK_PLATFORM="$NDK_ROOT/platforms/android-25" export PATH="$PATH:$NDK_ROOT/toolchains/llvm/prebuilt/darwin-x86_64/bin" export PATH="$PATH:$NDK_ROOT/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin" export PATH="$PATH:$NDK_ROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin" export PATH="$PATH:$NDK_ROOT/toolchains/x86-4.9/prebuilt/darwin-x86_64/bin" export PATH="$PATH:$NDK_ROOT/toolchains/x86_64-4.9/prebuilt/darwin-x86_64/bin"
You will probably need to tweak the paths and version numbers based on your operating system and the details of how you installed the Android NDK.
-
Install the
ant
build tool (usingbrew install ant
worked for me). -
Checkout the JNA source from Github. Try doing a basic build via
ant dist
andant test
. This won't build for Android but will test the rest of the tooling. -
Adjust
./native/Makefile
for compatibility with your Android NSK install. Here's what I had to do for mine:- Adjust the
$CC
variable to use clang instead of gcc:CC=aarch64-linux-android21-clang
. - Adjust thd
$CCP
variable to use the version from your system:CPP=cpp
. - Add
-landroid -llog
to the list of libraries to link against in$LIBS
.
- Adjust the
-
Build the JNA native libraries for the target platforms of interest:
ant -Dos.prefix=android-aarch64
ant -Dos.prefix=android-armv7
ant -Dos.prefix=android-x86
ant -Dos.prefix=android-x86-64
-
Package the newly-built native libraries into a JAR/AAR using
ant dist
. This should produce./dist/jna.aar
. -
Configure
build.gradle
for the consuming application to use the locally-built JNA artifact:// Tell gradle where to look for local artifacts. repositories { flatDir { dirs "/PATH/TO/YOUR/CHECKOUT/OF/jna/dist" } } // Tell gradle to exclude the published version of JNA. configurations { implementation { exclude group: "net.java.dev.jna", module:"jna" } } // Take a direct dependency on the local JNA AAR. dependencies { implementation name: "jna", ext: "aar" }
-
Rebuild and run your consuming application, and it should be using the locally-built JNA!
If you're trying to debug some unexpected JNA behaviour (and if you favour old-school printf-style debugging) then you can this code snippet to print to the Android log from the compiled native code:
#ifdef __ANDROID__
#include <android/log.h>
#define HACKY_ANDROID_LOG(...) __android_log_print(ANDROID_LOG_VERBOSE, "HACKY-DEBUGGING-FOR-ALL", __VA_ARGS__)
#else
#define HACKY_ANDROID_LOG(MSG)
#endif
HACKY_ANDROID_LOG("this will go to the android logcat output");
HACKY_ANDROID_LOG("it accepts printf-style format sequences, like this: %d", 42);