Android NDK (Native Development Kit) is a very popular tool used in application development for mobile devices. Many apps available in the market use a host of different programming languages apart from Java to achieve the best results. The NDK is hence basically a tool that helps programmers design their apps in native programming languages for a variety of reasons ranging from generating optimum performances to simplification of the code used.
Why and how native code is used
We are all aware of how application development in Android goes hand-in-hand with the Java programming language, and how it makes matters a lot easy for app developers — owing to Java’s elegant object-oriented programming design. Applications or algorithms written in Java compiles to its own bytecode which runs similarly across all platforms. Also, the JVM (Java Virtual Machine) that is responsible for JIT compiling and running Java bytecode is platform independent in the sense that it is available for operation on many platforms, everything from mainframes to mobile phones.
However, in terms of Android — since we are mostly dealing with smartphones and at the most tablets factors like effecting maximum performance out of the hardware and so on — become major factors. The Java source code, as mentioned above, first gets compiled into bytecode. It is the bytecode that runs later with alterations depending on the platform in which the particular JVM is running. Ultimately, the whole app runs inside a JVM on the Android device.
In the case of app development on Android, the above factor is seen to be a minor disadvantage. But programming in Java can also be a bit difficult because the code becomes a more and more complex to use and comprehend. Moreover, the whole platform independence-bytecode-JVM setup draws out a lot of the hardware’s performance.
Another important factor to consider is the multi-platform code. When we aim to create a to program with many platform targets, we might have to rewrite most of the controller and view the code for each platform, which is really not a very smart way. But almost all of the controller code could be ported using C or C++ because, again, almost all the mobile platforms support both languages; so if we can write the logic to some C-C++ code libraries and then reuse it in several target platforms that enables us to run the application without losing out on a lot of performance. In such cases, we are using C or C++ codes along with the default standard Java codes, hence the term “multiplatform code”.
While programming in native code, the source is compiled directly into the machine code for the CPU and not into an intermediate language like Java does. This is how app developers are able to get the best out of their apps in Android devices. These native code parts can be structured as a library that can be called from your Java code. A separate native library is also included for each CPU architecture you target. Most or all your native code will be target independent. The compiled native library gets embedded within your application’s .apk file. Having said that, the fundamental Android application model does not change.
Use of Android NDK
Android NDK is a set of tools that lets you implement parts of your Android app using native-code languages such as C and C++ and provides platform libraries that you can use to manage activities, and access the physical components of the device, such as the various sensors and display.
The Android NDK works with the basic standalone SDK tools as well as through the Android Studio IDE or with the older Eclipse ADT IDE. However, the NDK is not designed for use on its own.
How Android NDK works
The heart of the Android NDK package is the ndk-build script which is responsible for automatically going through our Android Project (every new Android app we build using an IDE like Android Studio or Eclipse, is built as a new Project) and determining what to build. The script is also responsible for generating binaries and copying those binaries to our app’s project path.
We can use the native keyword to let the compiler know that a particular code snippet used for implementation is native. For instance:
public native int numbers(int x, int y);
There are also Native Shared Libraries (in .so format) that the NDK builds from the native source code and Native Static Libraries (.a) which can be linked against other libraries. The Application Binary Interface (ABI) uses the .so files to know exactly how our application’s machine code will work with the system when the app is running.
Everything works under an interface known as the Java Native Interface (JNI), using which is how the Java and C/C++ components interact with each other.
When we are building an app using the ndk-build script, we need to create two files: Android.mk and Application.mk. Both these files go into the JNI directory. The Android.mk file defines the module plus its name, the build flags that libraries to link and what source files need to be compiled, whereas the Application.mk file describes the native modules that our app requires.
Installing and using the Android NDK in Ubuntu
Standard way through Linux terminal
Android’s NDK now ships as a self-extracting executable. We likely need to set the executable bit:
$ chmod +x android-ndk-r10c-linux-x86_64.bin
$ ./android-ndk-r10c-linux-x86_64.bin
The above will cause the NDK to extract into the current working directory.
Manual extraction
Since the .bin file is really just a 7-Zip self-extracting archive, we can manually extract the contents if needed by passing the following command:
$ 7za x -o/path/to/extract/to/ android-ndk-r10c-linux-x86_64.bin
7-Zip is available in Ubuntu for example via apt-get:
$ sudo apt-get install p7zip-full
Installation using Android Studio
We can also install NDK using the SDK Manager directly through Android Studio.
From an open project, select Tools > Android > SDK Manager from the menu bar. Click the SDK Tools tab. Check the boxes next to LLDB, CMake, and NDK. Then click to apply the changes.
Creating or importing a native project
Once we set up Android Studio, we can simply create a new project with C/C++ support. However, if we want to add or import native code to an existing Android Studio project, we need to follow this basic process:
Creating new native source files and adding them to our Android Studio project. We can skip this step if we already have native code or if we want to import a prebuilt native library.
Developing a CMake build script helps to tell CMake how to build our native sources into a library. We also require this build script if we are importing and linking against prebuilt or platform libraries. It is worth noting here that we can skip this step if our existing native library already has a CMakeLists.txt build script, or uses ndk-build and includes an Android.mk build script.
Link Gradle to our native library by providing a path to our CMake or ndk-build script file. Gradle uses the build script to import source code into our Android Studio project and package our native library (the SO file) into the APK.
Point to note: If our existing project uses the deprecated ndkCompile tool, we should open our build.properties file and remove the following line of code before configuring Gradle to use CMake or ndk-build.
android.useDeprecatedNdk = true
We can build and run our app by clicking the Run button. Gradle adds our CMake or ndk-build process as a dependency to compile, build and package our native library with our APK.
Once our app is running on a physical device or the emulator, we can use various IDEs such as Android Studio to debug our app.
All this highlights the importance of Android NDK for Android developers. The kit has given engine creators a great way to optimise their products for Android, enabling great graphical capabilities while using fewer resources.
Making a simple application using the Android NDK is not too difficult. However, it must be noted, very importantly that the Android NDK has some very specific use cases and probably should not be used in everyday development, either.
As much equally as it helps app development, Android NDK may not benefit the development of many apps. Notably, using native code on Android, in some cases, does not result in a noticeable performance improvement (it does improve performance in other cases), but it always increases the complexity of our apps. Commonly, performance gains can be appreciated in cases such as when the native code involves CPU-centric operations. But in general, one should only use the NDK if it is essential to the app — never because one simply prefers to program in C/C++.
As of something to conclude on the matter, there is no actual written rules or scruples to be followed as to when the NDK should and should not be used.