[!NOTE]
This article requires that you have already read part 1 of this article. If you haven’t read part 1 yet, please read it first, then come back to part 2.
Prerequisites
- Install & configure: Android NDK, and CMake
- Add your native dependency
- Set up linked CMake project for JNI bindings
Writing Binding Code
If you recall, we had a Concat.concat(args: Collection<String>): String
method in kotlin, and we had created a corresponding JNI function through Android Studio’s suggested action. That JNI function looks something like below.
|
|
First of all, we are going to remove the comment, and rename parameter thiz
to this
. Since we are using C, and not C++, we are allowed to use this
as an identifier name, we don’t need to give this parameter a weird name.
|
|
You’ll notice that all the parameters except env
, and even the return type of Java_com_example_concat_Concat_concat()
are Java objects a.k.a jobject
s. We can’t pass these values to functions in our C library, it expects C types. So, we need to
- Extract C structures from our
jobject
parameters. - Perform our calculations.
- Convert the result into a
jobject
, and return it.
[!NOTE]
Java primitive types e.g.jint
,jbyte
,jsize
, aretypedef
s of regular C types. So, values of those types can easily be passed around like any other primitive value in C.For example,
jsize
==jint
==int32_t
(C type)
Extracting C Structures From jobject
s
To access any property of a jobject
, or call any methods on it, we need to
-
Get a reference of the Java class of that object.
1
jclass collection_class = (*env)->GetObjectClass(env, collection_object);
-
Get the ID of a method from the Java class (properties are also treated as methods)
We need to pass method name and signature as well, in order to get method ID. Method signatures are explained in Method Signatures.
1
jmethodID collection_size_method = (*env)->GetMethodID(env, collection_class, "size", "()I");
-
Call the method
Calling a method also differs based on the return type. For primitive types, you use their specific methods, and for other types, you use
CallObjectMethod()
.1 2
jsize collection_size = (*env)->CallIntMethod(env, collection, collection_size_method); jobject collection_iterator = (*env)->CallObjectMethod(env, collection, collection_iterator_method);
Method Signatures
Method signatures are written in the form
(
Parameter Types )
Return Type
[!NOTE]
There are no separators among parameter types
-
Primitive types are represented by a single capital letter
Kotlin Type Representation Boolean Z Byte B Char C Short S Int I Long J Float F Double D -
Non-primitive types are represented in the form
L
Fully-Qualified Class Name (with/
in place of.
);
E.g.
Ljava/lang/String;
[!NOTE]
Notice;
at the end -
Java array types are prepended with a
[
E.g.
IntArray
would be represented as[I
So, signature of our function
|
|
would be
(Lkotlin/collections/Collection;)Ljava/lang/String;
[!NOTE]
Generics are ignored.
Extract C String Array From Java Collection<String>
Since we are dealing with string arrays here, we will create a struct
for them, so that passing around string arrays is little less cumbersome for us. We can definitely do just fine without the struct
, but having it is just easier.
[!NOTE]
Add this code to yourconcat-jni.c
|
|
We will also create a helper function to destroy our StrArray instances i.e. free the memory that was held by them.
|
|
[!NOTE]
Don’t forget to#include <malloc.h>
. We need it to create and destory instances of ourStrArray
struct.
StrArray_from_collection()
Retrieving C string array from a Java Collection<String>
is not a straightforward process. So, we will create a helper function StrArray_from_collection()
that converts a Java Collection<String>
object into our StrArry
type.
In case of a Collection<String>
, we need to
- Get
size
property - Call
iterator()
method - Loop through the iterator
- Convert each
jstring
object into a C string (const char*
)
|
|
Implement JNI Function
|
|
[!NOTE]
Don’t forget to#include <concat.h>
Use C code from Kotlin/Java code
Now, you can use the concat
library, and its function in your Kotlin/Java code.
|
|