# How to Use the Android NDK to Improve Performance

+ 2

Posted May 09 2011 01:23 PM

The Android Native Development Kit, or NDK, is an add-on to SDK that helps you integrate native code—code that uses platform-specific features, generally exposed through C or C++ language APIs—within your Android application. The main motivation for developing parts of your app in native code is performance. The following excerpt from Learning Android uses the Fibonacci algorithm to demonstrate the performance improvements you can achieve with the NDK.
Because the NDK is well-suited for computationally intensive applications, I wanted to find an example where we can implement a relatively simple algorithm in both native code and Java to compare their relative speeds.

So I picked a Fibonacci algorithm as the example. It’s a fairly simple algorithm that can be implemented easily in both C and Java. Additionally, we can implement it recursively as well as iteratively.

As a quick refresher, the Fibonacci series is defined as:

fib(0)=0
fib(1)=1
fib(n)=fib(n-1)+fib(n-2)

So the Fibonacci sequence looks like this: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, and so on.

In this example, we are going to:

• Create the Java class representing the Fibonacci library.
• Create the native code header file.
• Implement the native code by writing C code.
• Compile everything and build a shared library.
• Use this native code inside an Android activity.

FibLib

FibLib is where we declare our algorithms for computing the Fibonacci sequence. We have a total of four versions of the Fibonacci algorithm:

• Java recursive version
• Java iterative version
• Native recursive version
• Native iterative version

We’ll write the Java implementation in the example below and do the native ones in C later.

FibLib.java

package com.marakana;

public class FibLib {

// Java implementation - recursive
public static long fibJ(long n) {  //
if (n <= 0)
return 0;
if (n == 1)
return 1;
return fibJ(n - 1) + fibJ(n - 2);
}

// Java implementation - iterative
public static long fibJI(long n) { //
long previous = -1;
long result = 1;
for (long i = 0; i <= n; i++) {
long sum = result + previous;
previous = result;
result = sum;
}
return result;
}

// Native implementation
static {
}

// Native implementation - recursive
public static native long fibN(int n); //

// Native implementation - iterative
public static native long fibNI(int n);  //
}

At this point, our FibLib is complete, but we still need to back the native methods with their C implementations. To do that, first we need to create the appropriate JNI header file.

The next step is to create the C header file based on our FibLib Java file. To do that, we use Java’s standard javah tool. Note that you must have the Java Development Kit (JDK) installed in order to find this tool in the JDK/bin directory.

Now, to create the C header, go to your project’s bin directory and execute:

[Fibonacci/bin]> javah -jni com.marakana.FibLib

javah -jni takes a Java class as the parameter. Not all the classes are in the Java classpath by default, so it is easiest to just change directory to your project’s bin directory. Here, we assume that the current working directory is part of your Java classpath and thus that javah -jni com.marakana.FibLib at this location will work.

The result should be a new file named com_marakana_FibLib.h. This is the C header file that we need to implement next.

Before implementing our native files, let’s organize our project a little bit. Although Eclipse did a lot to set up our Android application directories in a meaningful way thus far, it doesn’t yet offer that level of support and automation for NDK development. We are going to do a couple of steps manually here.

For one, create a directory named jni inside your Eclipse Fibonacci project. This will be the place where you’ll store all your native code and related files. You can create this directory from within Eclipse by selecting the Fibonacci project in Package Explorer, right-clicking on it, and choosing New→Folder.

Next, move this new header file into that folder:

[Fibonacci/bin]> mv com_marakana_FibLib.h ../jni/

You can look into this file:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_marakana_FibLib */

#ifndef _Included_com_marakana_FibLib
#define _Included_com_marakana_FibLib
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     com_marakana_FibLib
* Method:    fibN
* Signature: (I)J
*/
JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibN
(JNIEnv *, jclass, jint);

/*
* Class:     com_marakana_FibLib
* Method:    fibNI
* Signature: (I)J
*/
JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibNI
(JNIEnv *, jclass, jint);

#ifdef __cplusplus
}
#endif
#endif

As you can see, this file is automatically generated and is not to be modified by the programmer directly. You may observe signatures for two of our native functions that we’re yet to implement:

...
JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibN
(JNIEnv *, jclass, jlong);
...
JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibNI
(JNIEnv *, jclass, jlong);
...

These are standard JNI signatures. They are generated by a naming convention indicating that the function contains code defined in Java as part of the com.marakana.FibLib class for the native methods fibN and fibNI. You can also see that both methods return jlong, a JNI-standardized integer value.

Their input parameters are also interesting: JNIEnv, jclass, and jlong. The first two are always part of a Java class, created to interface with native code. The JNIEnv points back to the virtual machine environment, and the next parameter points back to the class or object where this method is from; the parameter is jclass for a class method or jobject for an instance method. The third parameter, jlong, is just our input into the Fibonacci algorithm, or our n.

Now that we have this header file, it is time to provide its implementation in C.

C Implementation

We are going to create a C file that will implement our native algorithms. For simplicity’s sake, we’ll call it fib.c. Like the header file we looked at earlier, this file will reside in the jni folder. To create it, right-click on the jni folder and choose New→File. Save it as fib.c.

Note: When you open the C file, it might open up in another editor outside of Eclipse. That’s because the Java version of Eclipse typically doesn’t have support for C development. You could extend your Eclipse with C development tools by opening Eclipse and going to Help→Install New Software. Alternatively, you can just open the file with the standard Eclipse text editor by selecting the file and choosing Open With→Text Editor.

Next, we provide the implementation of the Fibonacci algorithm in C in this fib.c file, as shown below. The C versions of our algorithms are almost identical to the Java versions.

jni/fib.c

#include "com_marakana_FibLib.h" /*  */

/* Recursive Fibonacci Algorithm  */
long fibN(long n) {
if(n<=0) return 0;
if(n==1) return 1;
return fibN(n-1) + fibN(n-2);
}

/* Iterative Fibonacci Algorithm  */
long fibNI(long n) {
long previous = -1;
long result = 1;
long i=0;
int sum=0;
for (i = 0; i <= n; i++) {
sum = result + previous;
previous = result;
result = sum;
}
return result;
}

/* Signature of the JNI method as generated in header file  */
JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibN
(JNIEnv *env, jclass obj, jlong  n) {
return fibN(n);
}
/* Signature of the JNI method as generated in header file  */
JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibNI
(JNIEnv *env, jclass obj, jlong  n) {
return fibNI(n);
}

Now that we have implemented C versions of Fibonacci, we want to build the shared library. To do that, we need an appropriate makefile.

The Makefile

To build the native library, the Android.mk makefile must describe our files. The file is shown below.

jni/Android.mk

LOCAL_PATH := \$(call my-dir)

include \$(CLEAR_VARS)

LOCAL_MODULE    := fib
LOCAL_SRC_FILES := fib.c

include \$(BUILD_SHARED_LIBRARY)

The makefile is a part of the standard Android make system. All we are adding here is our specific input (fib.c) and our specific output (the fib module). The name of the module we specify is important and will determine the name of the library based on the operating system convention. For example, on ARM-based systems, the output will be a libfib.so file.

Once we have this makefile, we’re ready to initiate the build.

Building the Shared Library

Assuming you have the NDK installed properly, you can now build the native shared library by running ndk-build in your project directory. Here, ndk-build is a tool in the directory where your NDK is installed. We assume you put this directory into your environment PATH.

At this point, you should have a subdirectory named lib containing your shared library. When you deploy the Fibonacci application in the next section, this library is packaged as part of the APK.

Note: The shared library is compiled to run on the emulator by default, so it’s based on ARM architecture.

Finally, we need an application to put this library to good use.

The Fibonacci Activity

The Fibonacci Activity asks the user to input a number. Then, it runs the four algorithms to compute the Fibonacci value of that number. It also times the computation and prints the results to the screen. This activity basically uses the FibLib class that in turn uses libfib.so for its native part. The example below shows the code.

FibActivity.java

package com.marakana;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class Fibonacci extends Activity implements OnClickListener {
TextView textResult;
Button buttonGo;
EditText editInput;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// Find UI views
editInput = (EditText) findViewById(R.id.editInput);
textResult = (TextView) findViewById(R.id.textResult);
buttonGo = (Button) findViewById(R.id.buttonGo);
buttonGo.setOnClickListener(this);
}

public void onClick(View view) {

int input = Integer.parseInt(editInput.getText().toString()); //

long start, stop;
long result;
String out = "";

// Dalvik - Recursive
start = System.currentTimeMillis(); //
result = FibLib.fibJ(input);  //
stop = System.currentTimeMillis();  //
out += String.format("Dalvik recur  sive: %d (%d msec)", result,
stop - start);

// Dalvik - Iterative
start = System.currentTimeMillis();
result = FibLib.fibJI(input); //
stop = System.currentTimeMillis();
out += String.format("\nDalvik iterative: %d (%d msec)", result,
stop - start);

// Native - Recursive
start = System.currentTimeMillis();
result = FibLib.fibN(input); //
stop = System.currentTimeMillis();
out += String.format("\nNative recursive: %d (%d msec)", result,
stop - start);

// Native - Iterative
start = System.currentTimeMillis();
result = FibLib.fibNI(input); //
stop = System.currentTimeMillis();
out += String.format("\nNative iterative: %d (%d msec)", result,
stop - start);

textResult.setText(out); //
}
}

Testing That It All Works

At this point, we can fire up the Fibonacci application and run some tests on it. Keep in mind that larger values for n take quite a bit longer to process, especially using the recursive algorithms. One suggestion would be to keep n in the 25–30 range. Also keep in mind that we are doing all this processing on Activity’s main UI thread, and blocking that thread for a long period of time will lead to the Application Not Responding (ANR) error (see image below). As an exercise, you might want to move the actual calculation into an AsyncTask to prevent blocking the main thread.

Application Not Responding dialog

As you run some tests, you might notice that the native version of the algorithm runs about one order of magnitude faster than the Java implementation (see below).

Fibonacci of 33

These results alone should provide enough motivation to consider moving some of your computationally intensive code into native code. NDK makes the job of integrating native code into your app much simpler.

Tags:
1

## 4 Replies

: Dec 21 2012 02:35 AM
REMOVED
: Dec 22 2012 02:46 PM
Hello,

Thanks for the great post. I will really follow the steps and will contact you if need any further guidance.
Thanks and Regards,
Nishant Desai,
CEO & Founder
Kinsh Technologies

Professional Website Development | Professional SEO Services

http://www.kinsh.in
http://www.seoindianexperts.com
0

Posted Feb 19 2013 01:19 PM

Thats great post! Thanks.

http://www.sbwire.co...acts-208736.htm
0

Posted Jun 26 2013 11:47 AM

Hi sarahkim,

I know this thread is old/stale but I’m perplexed by one aspect of building the JNI Header file. Admit tingly I am a bit of a “newbie” when it comes to integrating Native Android development strategies and some of this is still “over my head”, but I thought my “discovery” was worth mentioning.

During the ndk-build process I kept on getting an error of the variety “conflicting types for…” (my fibN declaration) indicating a “previous declaration of …”(fibN) “was here”.

I was able to get passed this error by editing the (not to be edited) “machine generated file” and changing the JNI signature for the fibN method from :

JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibN
(JNIEnv *, jclass, jint);

To:

JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibN
(JNIEnv *, jclass, jlong);

Clearly I’m not going to go around editing machine generated files all day (and I’m sure it is not a “best practice” by any means).
Is there any way to specify (in the build process perhaps) some way of mapping the correct/desired type(jlong instead of jint) ? Does it make more sense to modify the fib.c file instead ?