Kotlin JNI for Native Code

How to call native code from Kotlin.

Matt Moore
3 min readJun 16, 2019

I’ve become a fan of TensorFlow for building machine learning models. But TensorFlow is currently not available for Kotlin. There is a Java wrapper that has been built but it doesn’t support the full TensorFlow library and requires an additional inter-oping with Java. I wanted a Kotlin wrapper directly over the C++ TensorFlow library.

I decided to start building it out myself. To that end, I needed to get JNI up and running within Kotlin. JNI allows us to access native libraries from within JVM languages, including Kotlin. Searching around the internet didn’t turn up a ton of info on using JNI with Kotlin specifically, so I’m sharing my own findings tinkering with it.

High-Level Overview

  1. First, we write our Kotlin code that calls the C/C++ code and compile it.
  2. Create a C/C++ header file for JNI to know how to talk to the native library.
  3. Then, we build our C/C++ code.
  4. Finally, we can run the Kotlin program with an option pointing to the C/C++ library we want to call.

The Kotlin Code

Here’s an example of how to build a basic Kotlin class that can call C code:

class NativeSample {
init {
System.loadLibrary("hello")
}
external fun sayHello()
}

We’ll save that in a text file called NativeSample.kt. Now, let’s create another file to run our main function in Main.kt:

fun main() {
NativeSample().sayHello()
}

Now we can compile these two files to NativeSample.jar:

kotlinc-jvm -include-runtime -d NativeSample.jar *.kt

The C Code

Now let’s create the C code that our Kotlin class will call. First, we’ll create a file called hello.c:

#include <stdio.h>
#include "NativeSample.h"
JNIEXPORT void JNICALL Java_NativeSample_sayHello(JNIEnv *env, jobject obj) {
printf("Hello World!\n");
return;
}

You’ll notice the #include "NativeSample.h" added to the second line of hello.c. We will need to create that header file. Note that the line that starts with JNIEXPORT has Java_NativeSample_sayHello. This needs to match our class and method in the NativeSample.kt file. It starts with Java, then follows with the name of our class NativeSample. Next it contains the method name sayHello.

Next, make a new text file NativeSample.h and add this to it:

#include <jni.h>#ifndef _Included_NativeSample
#define _Included_NativeSample
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_NativeSample_sayHello(JNIEnv *, jobject);#ifdef __cplusplus
}
#endif
#endif

Now we’re ready to compile our C code:

gcc hello.c -o libhello.so -shared -fPIC -I /usr/lib/jvm/java-12-openjdk-amd64/include -I /usr/lib/jvm/java-12-openjdk-amd64/include/linux

You’ll need to replace /usr/lib/jvm/java-12-openjdk-amd64 with your own JDK path. The above command will produce a file called libhello.so. This is our C library that prints Hello World!.

Running The Program

Now we’re ready to run Kotlin code we compiled earlier to call our C program:

java -jar NativeSample.jar

You should see:

Hello World!

Conclusion

You’ve now successfully compiled a Kotlin JVM program that calls a C library! This process is what I’m using to build out a Kotlin API around the C++ TensorFlow library. Stay tuned as I continue my saga of bringing TensorFlow to Kotlin!

--

--

Matt Moore
Matt Moore

Written by Matt Moore

Engineering leader in Chicago. Distributed Computing. Functional Programming. AI/ML.

Responses (4)