Overview of Java Native Access (JNA)
masterctypes or Windows' Platform/Invoke. Developers use a Java interface to describe functions and structures in the target native library, which JNA then invokes via a small JNI library stub.repository·master·Indexed 27 days ago
https://github.com/java-native-access/jnaA library that allows Java applications to call native shared libraries directly without writing JNI code. It uses Java interfaces to describe native functions and structures, supporting automatic mapping of primitive types, string conversion, complex types (Structures, Unions), and callbacks. The project includes JNA Core for basic binding and JNA Platform for cross-platform mappings, including Win32 and Linux system statistics via LibC and libudev.
ctypes or Windows' Platform/Invoke. Developers use a Java interface to describe functions and structures in the target native library, which JNA then invokes via a small JNI library stub.jna.jar to your project's CLASSPATH. The jna.jar includes the necessary native library (jnidispatch), which JNA will automatically extract and load. No additional configuration is required for the JNA library itself.To use JNA on Android, add the @aar dependency to your Gradle file and configure Proguard to prevent stripping JNA classes.
compile 'net.java.dev.jna:jna:4.4.0@aar'Proguard Rules:
-dontwarn java.awt.*
-keep class com.sun.jna.* { *; }
-keep class * extends com.sun.jna.* { *; }
-keepclassmembers class * extends com.sun.jna.* { public *; }This recipe allows building the FreeBSD x86-64 native library using QEMU. The process involves fetching a FreeBSD 13.2 amd64 image, resizing the disk, launching the amd64 emulator, installing prerequisites (OpenJDK 17, build tools, Apache Ant), transferring the JNA source, and running the build via Ant.
# Fetch image
wget https://download.freebsd.org/releases/VM-IMAGES/13.2-RELEASE/amd64/Latest/FreeBSD-13.2-RELEASE-amd64.qcow2.xz
xz -d FreeBSD-13.2-RELEASE-amd64.qcow2.xz
# Ensure there is enough space in the image
qemu-img resize -f qcow2 FreeBSD-13.2-RELEASE-amd64.qcow2 +5G
# Launch image
qemu-system-amd64 -m 4096M -drive file=FreeBSD-13.2-RELEASE-amd64.qcow2
gpart show /dev/ada0
gpart recover /dev/ada0
gpart show /dev/ada0
gpart resize -i 4 /dev/ada0
growfs /
# Exit single user mode (BSD boots to multi-user)
exit
# Login as root
# Set keyboard configuration
kbdmap
# Set current date and time (YYYYMMDDHHMM)
date 202403081928
# Install prerequisites - part 1 - java, build system, rsync
pkg install openjdk17 wget automake rsync gmake gcc bash texinfo
# Install prerequisites - part 2 - ant
wget https://dlcdn.apache.org/ant/binaries/apache-ant-1.10.14-bin.zip
unzip apache-ant-1.10.14-bin.zip
# Transfer JNA source code to build environment
rsync -av --exclude=.git USER@BUILD_HOST:src/jnalib/ jnalib/
# Build JNA and run unittests
cd jnalib
chmod +x native/libffi/configure native/libffi/install-sh
/root/apache-ant-1.10.14/bin/ant
# Copy jna native library back to host system
scp lib/native/freebsd-x86-64.jar USER@BUILD_HOST:src/jnalib/lib/nativeThe standard way to map native functions is to create a Java interface that extends com.sun.jna.Library (or com.sun.jna.StdCallLibrary for Windows __stdcall conventions).
Inside the interface, define a static INSTANCE using Native.load(String libraryName, Class<T> interfaceClass). You can also use Native.synchronizedLibrary(instance) to wrap calls in a synchronized block, ensuring only one native call occurs at a time.
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary) Native.load((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);
void printf(String format, Object... args);
}
// Usage
CLibrary.INSTANCE.printf("Hello, World\n");JNA provides platform.jar, which contains cross-platform mappings and common platform functions (especially Win32). Before mapping your own native functions, check the com.sun.jna.platform package to see if they are already implemented.
Mapping Conventions:
ShlObj.h structures are in com.sun.jna.platform.win32.ShlObj.Advapi32.dll functions are in com.sun.jna.platform.win32.Advapi32.Util. Example: com.sun.jna.platform.win32.Advapi32Util.Available Cross-Platform Utilities (com.sun.jna.platform):
FileMonitor: File system watcher.FileUtils: File operations (e.g., moving to recycle bin).KeyboardUtils: Keyboard state functions.WindowUtils: Window management (e.g., transparent/non-rectangular windows).To build a static library for ARM64 using a Visual Studio solution:
msvc_build/aarch64/Ffi_staticLib.slnmsvc_build/aarch64/aarch64_include/JNA supports a direct mapping method that can substantially improve performance, approaching the speed of custom JNI. Unlike standard interface mapping, you define native methods directly within a class (as static native or instance methods) and register them using Native.register() inside a static initializer.
Key Constraints:
Pointer, Structure, String, WString, or NativeMapped as function arguments.NativeMapped.Integer, Double) can only be used if a custom TypeMapper is provided. Since direct mapping is intended for performance, using wrappers is discouraged due to overhead.import com.sun.jna.*;
public class HelloWorld {
public static native double cos(double x);
public static native double sin(double x);
static {
// Native.register() takes the name of your native library,
// same as Native.load() would.
Native.register(Platform.C_LIBRARY_NAME);
}
public static void main(String[] args) {
System.out.println("cos(0)=" + cos(0));
System.out.println("sin(0)=" + sin(0));
}
}To develop against JNA without rebuilding the JNA JAR file between every code change, you can set up a local module in IntelliJ IDEA. This approach uses the idea-jar target to provide the necessary native components.
idea-jar target (via Ant) to generate a JAR containing all native bits required by JNA.src folder as Sources and the testsrc folder as Test Sources.lib directory.lib/test directory.idea-dispatch.jar generated by the idea-jar target.Once configured, you can use JNA in your own code via this module instead of including a standard JNA JAR, which significantly speeds up the development cycle.
When a C function accepts a pointer-to-type argument (e.g., void func(int* p)), you can use JNA's ByReference types to capture the value returned by the function. This allows the native code to write a value into the memory address provided by Java.
Commonly used types include:
PointerByReference: For void** or similar pointer-to-pointer types.IntByReference: For int* types.While you can use a single-element Java array as an alternative, using ByReference classes is the recommended convention as it more clearly conveys the intent of the code.
Use Apache Ant to build JNA for specific Android architectures by passing the os.prefix property.
To build for a specific architecture, use the command: ant -Dos.prefix=<architecture_prefix> dist.
Common prefixes include:
android-armandroid-armv7android-aarch64android-mipsandroid-mips64android-x86android-x86-64When a native function requires a pointer to a struct, use a Java class that extends Structure.
To ensure correct memory layout, you must specify the order of the fields. This is done by either:
@FieldOrder annotation with the field names in order.getFieldOrder() method to return a list of field names in order.It is a best practice to define these structures as public static classes within your library interface definition so they can inherit custom type mappings defined for that interface.