Hello jni
References:
./code/jni/hello/Makefile
CXXFLAGS := -I $(JAVA_HOME)/include
CXXFLAGS += -I $(JAVA_HOME)/include/linux
all: NativeSample.jar libhello.so
NativeSample.jar: NativeSample.kt Main.kt
kotlinc-jvm -include-runtime -d NativeSample.jar NativeSample.kt Main.kt
libhello.so: hello.cc hello.h
$(CXX) -o $@ -shared -fPIC $(CXXFLAGS) hello.cc
run: all
java -jar NativeSample.jar
clean:
$(RM) *.jar *.so
Note: We have to disable name mangling here.
https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/design.html describe the specification:
Java_NativeSample_sayHello
: The prefix is fixed withJava
.NativeSample
is the class name in kotlin.sayHello
is the method in kotlin.
./code/jni/hello/hello.h
#ifndef HELLO_H
#define HELLO_H
#include "jni.h"
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_NativeSample_sayHello(JNIEnv *env, jobject obj);
JNIEXPORT jfloat JNICALL Java_NativeSample_sum(JNIEnv *env, jobject obj,
jfloatArray array);
#ifdef __cplusplus
}
#endif
#endif // HELLO_H
The first argument must be JNIEnv *env
. Since it is a non-static kotlin method,
the second argument is a reference to the object. Otherwise, it is a reference
to the class.
./code/jni/hello/hello.cc
#include "hello.h"
JNIEXPORT void JNICALL Java_NativeSample_sayHello(JNIEnv *env, jobject obj) {
std::cout << "hello world\n";
}
JNIEXPORT jfloat JNICALL Java_NativeSample_sum(JNIEnv *env, jobject obj,
jfloatArray array) {
// the last arg is set to nullptr, which means we are not interested
// in whether the returned pointer points to a copied region or not.
jfloat *p = env->GetFloatArrayElements(array, nullptr);
jsize n = env->GetArrayLength(array);
jfloat s = 0;
for (int32_t i = 0; i != n; ++i) {
s += p[i];
}
// See
// https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html
// JNI_ABORT: means to free the buffer pointed by p but don't copy the
// buffer to array
env->ReleaseFloatArrayElements(array, p, JNI_ABORT);
return s;
}
System.loadLibrary("hello")
:
On Linux, it looks for
libhello.so
On Windows, it looks for
hello.dll
external fun sayHello()
:
It will look for the function with name
Java_NativeSample_sayHello
.
./code/jni/hello/NativeSample.kt
class NativeSample {
companion object {
init {
System.loadLibrary("hello")
}
}
external fun sayHello()
external fun sum(array: FloatArray): Float
}
./code/jni/hello/Main.kt
fun main() {
var s = NativeSample()
s.sayHello()
var a : FloatArray = listOf(1.2f, 3.0f).toFloatArray()
println(s.sum(a)) // 4.2
a = floatArrayOf(3.0f, 4.0f, 5.0f)
println(s.sum(a)) // 12.0
a = FloatArray(100) {it.toFloat()}
println(s.sum(a)) // 4950.0
}