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 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:
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:
- 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.