Search code examples
javaandroidandroid-ndkjvmjava-native-interface

How does NDK work in Android - What is the order that NDK, JNI etc are used?


How are: Java, NDK, JNI, JVM, C/C++ code etc. related to each other in an android project?

I want to know what happens e.g. Java code runs NDK method, NDK uses JNI library or whatever happens. Also what is the difference between NDK and JNI

Sorry if this question is poorly worded i'm just having difficulties how NDK is implemented in an Android project


Solution

  • The NDK is used to compile C/C++/asm code into binaries. You can do a lot of things with the NDK, like compiling executables, static prebuilts... but in the end, in the context of an Android application, you obtain one or more .so files (shared object libraries).

    From Java, you can load such .so files and map functions that are implemented inside them. There is a specific case where an Android application project contains only C/C++ code (see native-activity sample from the NDK), but this is still the same principle: .so files are loaded inside ART or Dalvik context.

    Anything in an Android application is executed in the context of the Dalvik Virtual Machine or ART. And in order to integrate your C/C++ code with this environment, you use the JNI (Java Native Interface).

    You use the JNI first from the JNI mechanism: when you load a .so file, its JNI_OnLoad() function is called if it exists and functions starting with Java_ are mapped to the corresponding Java methods. These methods are to be declared in Java classes and have the keyword native to specify that their implementation comes from a .so file. A .so file is loaded using System.loadLibrary(), usually from the static block of the Java class that have the methods declared as native.

    At runtime, when the Java execution reaches a method with its implementation being native, ie. inside a .so file, and that .so file has been loaded, this native implementation is directly executed. When it returns, the Java code execution continues.

    The JNI also gives you jni.h header, that gives you the methods to access the Java environment (JavaVM*, JNIEnv*) so you can manipulate, create and access Java primitives (jint, jlong...), objects (jobject, jclass..), exceptions (jthrowable), etc.

    To be properly packaged, installed and loaded using System.loadLibrary, your .so files have to be put at specific places:

    • libs/CPU_ABI inside an eclipse project

    • jniLibs/CPU_ABI inside an Android Studio project

    • jni/CPU_ABI inside an AAR

    When an application is packaged as an APK, libs are inside its lib/CPU_ABI folders.

    During its installation, the libs for the target platform are installed into nativeLibraryPath on a <5.0 device, and inside the app's legacyNativeLibraryDir/CPU_ARCH on a >=5.0 device.

    Where CPU_ABI is any of: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64. Depending on which architectures you're targeting and your libs have been compiled for.