Android NDK. Calling Kotlin from native code

In the previous article, we made out the base theoretical part about Android NDK. We saw how to make Kotlin communicate with C/C++. Kotlin can call the C/C++ code with any data types. We converted String’s instances and passed Kotlin’s objects to the native code, called functions and threw Exceptions of JDK.

In this article, we are going to find out how to call Kotlin from native code.

Why do we need to call Kotlin from native code?

The most obvious reason is the callbacks. When we perform some operation, we want to know when it’s done and receive the result. It also relates to JNI. For instance, let’s imagine that we want to know when a new value is persisted to our in-memory storage, which was created in the previous article.

Calling static properties

JNIEnv contains many functions to deal with static fields. Firstly, you have to get the id of static field using jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig); function.

As you have probably known, Kotlin does not have static members, but each class of Kotlin can have companion object, and we can similarly use its fields and functions. A companion object is initialized when the corresponding class is loaded, matching the semantics of a Java static initializer. Thus, we can use it to load native libraries:

We can create a field in a companion object:

JNIEnv contains different functions to access a value of a static field from native code:

http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp5901

So, we can get the int value as follow:

To determine a method’s signature, we use type codes. The following table summarizes the various types available in JNI with their code:

As you can see bellow, the compiler generates theprivate static field in class Store. Please, notice that we can access value, despite private modifier.

If you just create this field in simple class, you get the exception:java.lang.NoSuchFieldError: no “I” field “static” in class “Lcom/ihorkucherenko/storage/Store;” or its superclasses. If you use Int? type, you also receive this error.

Calling static methods

To get the id of the static method you have to use jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
. JNIEnv contains tree types of functions, which can call static methods:

1.NativeType CallStatic<type>Method(JNIEnv *env, jclass clazz,
jmethodID methodID, ...);

2.NativeType CallStatic<type>MethodA(JNIEnv *env, jclass clazz,
jmethodID methodID, jvalue *args);

3.NativeType CallStatic<type>MethodV(JNIEnv *env, jclass clazz,
jmethodID methodID, va_list args);

As you can, they differ in the arguments. So, we have next set of functions:

http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp20949

Let’s try it.

We have to use @JvmStatic to generate additional static method. You can see it in bytecode:

Please, pay attention to this: (I)I. It’s JNI’s representation of type’s signature.

In parentheses, types of arguments are specified. Several samples:

  1. Kotlin:
@JvmStatic
fun staticMethod(int: Int, floaf: Float) = int

Native code:

public final staticMethod(IF)I

2. Kotlin:

@JvmStatic
fun staticMethod(int: Int, floaf: Float, string: String) = int

Native code:

public final staticMethod(IFLjava/lang/String;)I

After parentheses follow return type.

To call this function from the native code, we have to write something like this:

Calling instance’s properties

Similar to previous examples we will use functions to retrieve the id of field and value:jfieldID GetFieldID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
.

Let’s add private val property = 3 to Store class. It equals to private final I property = 3 in bytecode. Now we have to get jobject that refers to the instance of Store in order to retrieve the value from property. For example, we can create new jobject inside the native code and get the value:

Calling instance’s methods

There is no significant difference in the calling static and instance’s method. So, let’s move to sample:

As you have probably noticed, differ in the fact that we have to have jobject to pass it to CallIntMethod.

The sample of callback

Let’s add the simple callback to our Store:

We invoke these methods from proper native functions:

Let’s test it:

Conclusions

In this article, we extended the in-memory store sample. We found out how to reсeive a value from an instance’s and static field of Kotlin in native code, how to call Kotlin methods using CallStatic<Type>Method and Call<Type>Method functions and how to create a new jobject in C/C++. We augmented our new knowledge by the creation of a simple callback.

So, we can make conclusions that Kotlin is compatible with Android NDK and JNI.

Like what you read? Give Ihor Kucherenko a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.