Understanding Android NDK with Kotlin: Episode 1
An Android developer’s day life is full of challenges, from updates in the ecosystem, migration to Kotlin to using Dagger2, RxJava and so on. On my journey to becoming a better developer, I got a chance play with the NDK on one of the projects. I was excited to work on it but at the same time, worried because of the lack of documentation.
Through this article I’ll be sharing my experiences with the NDK and how to use it with Kotlin.
What and Why
The Native Development Kit (NDK) is a set of tools that allow you to use C and C++ code in your Android app. It provides [platform libraries](https://developer.android.com/ndk/guides/stable_apis.html) to manage native activities and access hardware components such as sensors and touch input.
The NDK may not be appropriate for most novice Android programmers who need to use only Java code and framework APIs to develop their apps. However, the NDK can be useful for the following cases:
- Squeeze extra performance out of a device to achieve low latency or run computationally intensive applications, such as games or physics simulations.
- Reuse code between your iOS and Android apps.
- Use libraries like FFMPEG, OpenCV, etc.
Java Native Interface
JNI is the medium of interaction between the Java runtime and native code. It involves more than one language and runtime so some familiarity with C/C++ is recommended.
- Create a new project
- Include C++ and Kotlin support
- Download the latest Android NDK, CMake and LLDB from the SDK manager.
CMake and LLDB
CMake is cross-platform, free and open-source software for managing the build process of software using a compiler-independent method. The build project contains a CMakeLists.txt file in every directory that controls the build process. List of CMake useful functions here. LLDB is a next generation, high-performance debugger. LLDB currently converts debug information into clang types so that it can leverage the clang compiler infrastructure. It allows LLDB to support the latest C, C++ language features and runtimes in expressions without having to reimplement any of its functionality. JNI Primitive Types JNIhas its own primitive and reference data types. To work with native code (call native functions written in C/C++, pass arguments, get results, etc.) primitive types are used.
JNI Function Example
To call a native function from Kotlin we’ll have to add: `extern “c”` `JNIEXPORT` macro: A macro is a fragment of code which has been given a name. Whenever the name is used, it is replaced by the contents of the macro.
The native function should have at least two arguments: `JNIEnv*` pEnvand `jobject` jObj. The `JNIEnv*` is a pointer to the pointer to function tables. It provides most of the JNI functions. All your native functions will receive a `JNIEnv` as the first argument. `jobject` jObj is the class instance that contains this function in Kotlin code. In my case it’s an object of Math class. JNI functions are available through an *interface pointer*. An interface pointer is a pointer to a pointer. This pointer points to an array of pointers, each of which points to an interface function. Every interface function is at a predefined offset inside the array.see figure below for the illustration.
Adding Header File
To add header files we can either use`#include<filename>`to include standard library header files or`#include "filename"`to include programmer-defined header files.
To avoid including headers multiple times by mistake, wrapping the header content in`#ifndef — #endif`can be used, this avoids error during compilation.
JNI — Reference Types
To get an instance of Kotlin class we have to use the above mentioned reference types.
Local, Weak and Global References
We can create local, weak and global references with
JNIEnv *jEnvprovides most of the JNI functions
jobject NewGlobalRef(JNIEnv *env, jobject obj);,
jweak NewWeakGlobalRef(JNIEnv *env, jobject obj);and
jobject NewLocalRef(JNIEnv *env, jobject ref);
respectively for creating references.
Similarly we have functions to delete the reference:
void DeleteLocalRef(JNIEnv *env, jobject localRef);,
void DeleteGlobalRef(JNIEnv *env, jobject globalRef);and
void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);.
Android.mk and Application.mk
Android.mkis themakefilefor building the native project. It allows you to group your sources into*modules.*A module is either a static library, a shared library, or a standalone executable. You can define one or more modules in each
Android.mkfile, and you can use the same source file in multiple modules.
Application.mkfile is really a tiny GNU Makefile fragment that defines several variables for compilation.This makefile describes several variables that will help make the assembly more flexible.
This covered most of the basics required to start using Native SDK in your project.
In this article we went through the basics of Android NDK. If you liked this post, hit 👏 . Do hop in for the Episode-2 where I’ll demonstrate a sample app using the NDK where we will see how to communicate between Kotlin and C++
originally posted in Medium