Android and the Java Native Interface

From RidgeRun Developer Connection
Jump to: navigation, search

Introduction

This wiki shows the main aspects of the Java Native Interface (JNI) and its usage on the RidgeRun's Mobile Framework

The following readings are recommended to better understand what we are talking here:

https://developer.android.com/ndk/index.html

https://developer.android.com/ndk/samples/sample_hellojni.html

http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html

https://developer.android.com/training/articles/perf-jni.html

http://web.mit.edu/javadev/doc/tutorial/native1.1/implementing/index.html

https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html

What's Java Native Interface?

As the above web pages explained, the JNI is a way of writing Native code, say C/C++, inside routines, methods, classes and others of Java source code. This allows the reuse of already-coded libraries into the Virtual Machine of Java. While its usage is not recommended by Google (not completely true), it provides a proper way to handle stuff that otherwise would be impossible with such as a high-level language like Java.

How Android uses JNI?

Android has a set of tools that allows the usage of the JNI, it is called Native Development Kit (NDK) and has a wide variety of helpful tools, like toolchains, libraries, and so others. In Android Studio the Android NDK is easily integrated by downloading it and setting the path of it inside Studio.

Mobile Framework and the JNI

The framework we have created uses JNI in almost every part of the code. This was needed as the media client we had in mind was only possible using GStreamer. As you may already know, GStreamer is written in C and therefore the need of using the JNI.

JNI Set up for the Mobile Framework

The set up of the JNI may be a little tricky. It was difficult when Eclipse IDE was used, and it became even more difficult on Android Studio because of the Gradle files. The good news is that once it is configured, almost none further interaction is required.

In any kind of application, the build.gradle files must be modified when native code is intended to be built within the IDE. On the Mobile Framework we tried to separate the build of the native code and the Java one, nevertheless it was not possible, therefore we build everything within Android Studio.

Gradle File Modifications to allow JNI build

Once you have downloaded the NDK, determine the path of it and have at hand.

On the build.gradle of the application module, usually at $APPLICATION/app/build.gradle, you must set up the directory where to look at the sources coded in C. That is accomplished by writing the next line:

sourceSets.main.jni.srcDirs = [<PATH_TO_SOURCES>]

Once that is set, then a new 'task' must be written stating what kind of action it would do. For instance when trying to build a C code using the NDK something like this must be added:

task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {

        def ndkDir = '/home/rjimenez/Android-NDK/android-ndk-r11b'
        commandLine "$ndkDir/ndk-build",
                'NDK_PROJECT_PATH=build/intermediates/ndk',
                'NDK_LIBS_OUT=src/main/jniLibs',
                'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
                'NDK_APPLICATION_MK=src/main/jni/Application.mk'
    }

What this does is to define a task called 'ndkBuild' which is an execution, that will look at the path 'ndkDir' and defines a command called 'commandLine' which is the concatenation of all the variables the are next to it. To simplify it, the resulting 'commandLine' would be:

        commandLine = "/home/rjimenez/Android-NDK/android-ndk-r11b/ndk-build", 'NDK_PROJECT_PATH=build/intermediates/ndk', 'NDK_LIBS_OUT=src/main/jniLibs', 'APP_BUILD_SCRIPT=src/main/jni/Android.mk', 'NDK_APPLICATION_MK=src/main/jni/Application.mk

All of those macros are needed to properly by the NDK build system to properly configure and build the native code.

The above command must be executed at some place during the Java compilation, therefore we add the next routine on the same file:

    tasks.withType(JavaCompile) {
        compileTask -> compileTask.dependsOn ndkBuild
    }


This tells the Java Compilation that there is a task that must be executed, and that task depends on the proper definition of 'ndkBuild'. By default, the C sources are searched under:

$APPLICATION/app/src/main/jni/

With all of these defined, then on the source code side the Android.mk and the Application.mk makefiles shall be created, this is on the jni folder described above.