Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

An Introduction to Android NDK

4.49/5 (16 votes)
30 Sep 2014CPOL5 min read 33.2K   408  
Basic NDK implementation using Hello World application

Table of Contents

Preface

This tutorial will not cover an NDK installation/configuration on your machine. Instead, it will explain how to set up the very basic integration between Java and NDK using simple “Hello World” example.

Why NDK?

The NDK (Native Development Kit) allows you to write code with C/C++ languages and then call it from your Java application using JNI (Java Native Interface).

Using native code in Java is reasonable especially when you are dealing with bits/bytes operations, like bitmaps compression/decompression.

NDK will not be interpreted like Java through the JVM, instead it will use the operating system API (in Android case, Linux) for those operations, and thus its performance will be much faster.

One of the big advantages of the NDK is that you can call custom allocation of memory using malloc() method. However, in the native code, there is no GC (Garbage collection), hence you need to free memory by yourself.

Potentially, you can increase your application performance, but sometimes it can be just overkill, so use it appropriately.

Image 1

General Steps for Java-NDK Integration

  1. Declare native method in Java class.
  2. Create a header file according to native implementation.
  3. Implement the header file on the native side in C/C++ class.
  4. Create an Android.mk file.
  5. Compile native code to .so package/s.
  6. Call the Java native declaration method.

Step 1: Declare Native Method in Java Class

First thing that we need to do is to create a native method declaration in our Android application class.

Java
public class NDK_Methods
{
     public static native String SayHello();
}

Step 2: Creating Header File

What is Javah?

Javah is a Java utility that produces C headers files from Java class, in order to provide an interface through which Java and C code can interact.

Creating a Header File using javah Utility

After we created our native SayHello() method declaration, we need to create a header file which will be implemented in C class on the native side.

Steps:

  1. Go to the root directory of your project.
  2. Open a CMD.
  3. Type in"
    javah -classpath bin/classes/ -d jni/ndk.NDK_Methods
  • -classpath bin/classes/: location of the classes directory in which the compiled Java classes of your Android application are located.
  • -d jni/ndk.NDK_Methods: is a fully qualified name of the class from which the header class will be generated.

The generated header file will be created under the jni directory in our project.

Image 2

In our example, we will get the following header file:

C++
/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class ndk_NDK_Methods */
#ifndef _Included_ndk_NDK_Methods
#define _Included_ndk_NDK_Methods
#ifdef __cplusplus
extern "C" {

#endif

/*
 * Class:     ndk_NDK_Methods
 * Method:    SayHelo
 * Signature: ()Ljava/lang/String;
 */

JNIEXPORT jstring JNICALL Java_ndk_NDK_1Methods_SayHelo(JNIEnv *, jclass);

#ifdef __cplusplus

}

#endif

#endif

Our Java method declaration:

Java
public static native String SayHello();

was generated to:

C++
JNIEXPORT jstring JNICALL Java_ndk_NDK_1Methods_SayHelo (JNIEnv *, jclass);

Notice that the generated name is Java followed by package name, class and the method separated by underscores.

Since we did not pass any parameters to the function, we accept only the default parameters:

  1. JNIEnv – is a pointer through which we are communicating with Java from the native implementation.
  2. jclass – a class that called the function.

Step 3: Implementing the Header File

Now we have a header file and we need to define an implementation for our method.

We need to create a new file with c or cpp extention  in the jni directory and copy to it the SayHello() method declaration:

Java
//specifies the header file that is included
#include <ndk_NDK_Methods.h>

JNIEXPORT jstring JNICALL Java_ndk_NDK_1Methods_SayHello(JNIEnv *env, jobject thiz )
{
         return (*env)->NewStringUTF(env, "Hello from JNI!");
}

Notice that we are returning jstring which is different from C string in our method. We need to return a Java object, so we are doing it through the JniEnv pointer by calling a NewStringUTF() method.

Step 4: Creating an Android.mk File

In order to compile our file to .so package, we need to define an Android.mk file in our jni directory.

Android.mk file describes to the build system about your sources.

Android.mk contains those fields:

  • LOCAL_PATH := $(call my-dir) - Android.mk must begin with this definition, this defines the location of the sources. The macro ‘my-dir’ is the directory of the Android.mk file.
  • include$(CLEAR_VARS)clears all variables that might be set from a previous module build.
  • LOCAL_MODULE :=native_lib – sets the name that is used as the identifier for a module, which is later used in Java.
  • LOCAL_SRC_FILES := native_lib – file that will be compiled in your module, no need to specify headers, the system will take care of it.
  • include $(BUILD_SHARED_LIBRARY) – ensures that shared library becomes a part of this make. 

Application.mk

Not necessary for the compilation, but in our case we specified:

  • APP_ABI := all – sets the compilation for all the supported CPUs

If we will not specify this, the compilation will be made only for the default CPU. You can also specify your CPU destination compilation explicitly.

Image 3

Step 5: Compiling the Native Code

What is ndk-build?

An ndk-build is an NDK tool which is responsible for compiling your native code to executable files.

Compilation

After we created the Android.mk file, we can create the .so package using ndk-build tool.

Steps:

  1. Go to your root directory of the android application.
  2. Lunch the ndk-build utility with its full path, in my case: C:/android_ndk/ndk-build.

C:/android_ndk – The directory where you have downloaded your NDK.

ndk-build – The utility.

This will create a .so package/s in libs directory in our project.

Step 6: Calling Native Method

What’s left for us to do is to make a call from Java to JNI method in our application:

Java
String ndkMessage = NDK_Methods.SayHelo();

This will call the static method in NDK_Methods class, that will call the Java_ndk_NDK_1Methods_SayHelo() method which is declared in the header file and implemented in our C class. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)