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:
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 the
private static field in class Store. Please, notice that we can access value, despite
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:
NativeType CallStatic<type>Method(JNIEnv *env, jclass clazz,
jmethodID methodID, ...);
NativeType CallStatic<type>MethodA(JNIEnv *env, jclass clazz,
jmethodID methodID, jvalue *args);
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:
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:
fun staticMethod(int: Int, floaf: Float) = int
public final staticMethod(IF)I
fun staticMethod(int: Int, floaf: Float, string: String) = int
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);
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
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:
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
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.