Boosting Android performance using JNI

JNI or Java Native Interface is the interface between the Java code running in a JVM and the native code running outside the JVM. It works both ways, that is you can use JNI to call native code from your Java programs and to call Java code from your native code. The native code normally resides within a library (.so file) and is typically written in C/C++.

There’s a few reasons when to use JNI in a Java program

  • Wrap lower level functionality – Classes in the Android Application Framework uses JNI to interface with the underlying platform and hardware like Camera, Sensors and GPS.
  • Interface native legacy code – You might have some old legacy code written in C/C++ and you don’t want to waste your time porting the code to Java. With JNI you can create an interface class in Java that exposes the functionality of your legacy code.
  • Bypass performance bottlenecks – Execute heavy number crunching in native code and get rid of the overhead that the instruction interpretation in the JVM introduces.

On Android, in order to prevent fragmentation, we are only allowed to use the following libraries in our native code.

  • libc (C library) headers
  • libm (math library) headers
  • JNI interface headers
  • libz (Zlib compression) headers
  • liblog (Android logging) header
  • OpenGL ES 1.1 (3D graphics library) headers (since 1.6)
  • A Minimal set of headers for C++ support

In this blog I will demonstrate how to transform a time consuming Java method with a lot of number crunching into a native declared method where the real work is performed in native code. Here’s the time consuming Java method

The sourceData and targetData arguments represents the pixels of two Bitmaps. In short the method calculates the sum of the square distance in color between two images, pixel by pixel. If we compare two 200×200 pixels images the for-loop will run 40000 times! This is a typical candidate for when to use JNI.

What to do in Native

You can use the boilerplate.c file as boilerplate code for all your native c functions.

Implement the native function

This is what the function will look like when written in C.

All native functions must have the JNIEnv (a reference to the virtual machine itself) and the jobject (a reference to the “this pointer” of the Java object where the native method call comes from) as the first two arguments. Then we can add our own arguments. For more information on how to write native code for JNI see the JNI Reference Documentation.

Register your native functions

We need a way to make the virtual machine direct the calls to the native declared Java method to our native C function. This is done using the registerNatives function of the JNIEnv. If you use the boilerplate C code from above you only have to do two things.

  1. Set the classpath variable to the full class name of your Java class (including package name). Replace the dots with slashes.
  2. For each native declared method in Java, insert a JNINativeMethod struct into the methods[] array.

For our example it will look like this

The first parameter is the name of the native declared Java method, the second is the signature of the native declared Java method and the last parameter is the function pointer to the C function to execute when the native declared Java function is executed.

The signature of a Java method can be determined using the javap tool from SUN’s Java SDK or you can create it yourself using the following table, Java VM Type Signatures.

Build using Android NDK

To make things really simple when developing JNI code Google has released the Android Native Development Kit (NDK). It’s easy to setup and use. Just setup a new project according to the NDK documentation and build your project. In short, you create a folder named jni in your Android Eclipse project. Here you put all the c-files together with an Android.mk file. In the Android.mk you specify which c-files to compile. In the Android NDK/apps folder you create a directory named after your project (perhaps my-app). In this directory you add an Application.mk file. In the Application.mk, set the APP_PROJECT_PATH to the path of your Eclipse project. To create the lib simply type make APP=my-app from the console in your Android NDK folder. If everything goes well you will see a libs folder in your Eclipse project.

What to do in Java

In order to transform our Java method into a native declared method simply add the word native in the method signature and remove the implementation of the method.

To make sure that the virtual machine loads our library into memory and register our native functions we have to add the following code.

This tells the virtual machine to load the library libcomparejni.so from the underlying operating system. The OnLoad function is executed on the library and our native functions gets registered with the virtual machine.

The Result

After some simple benchmarking I found that the native declared method executed about 2-3 times faster than the original method executing within Dalvik. For larger images the improvement might be even bigger since a call to a native declared method takes more time than calling a normal Java method. Eventually, when Dalvik supports JIT compilation, we would probably get the above performance boost for free without calling native functions. But bear in mind, most devices on the market today will never get an upgraded Dalvik with JIT.

14 Responses to “Boosting Android performance using JNI”

  1. AG says:

    JNI should be a last resort. Your app doesn’t magically get faster just because you use JNI. A quadratic algorithm doesn’t get linear just because you wrote it using c++. Also, interacting with Java objects from within c++ is usually slower than interacting with Java objects from Java.
    Do not misunderstand me: JNI is a good thing, but you must know how (and when) to use it.

  2. sriram says:

    Hi I have a requirement in which i need to send the byte[] received in android.hardware.camera preview callback to a .cpp file through JNI, without making a copy of the array. Can you please suggest me the suitable methods to do that. After a research in the web i found NewDirectByteBuffer and GetDirectBufferAddress are the calls i have to use. But i am not sure how to use them. A help is highly appreciated.

  3. Abdullah says:

    Jay Way team always write great tutorials. I learnt OpenGL ES from this blog and now another great tutorial

  4. masood says:

    Hi

    how to render a square using native code in android

  5. Jon Schell says:

    I’m trying to make an app that actually interacts with the hardware, so I need my c code to call a specific function that exists in the phone image. Do you have any idea how to compile this using the NDK? Rather, it will compile, but it won’t link due to an undefined reference. Alternatively, do you know of any other resources that would be able to help me?

  6. karthi says:

    Hi ,
    the application will boost up means, how much the work performance it show, what difference will happening while we using the JNI concept for android. Could you explain me that please……

  7. Mattias Rosberg says:

    Say we want to create a Mandelbrot image as a java Bitmap. We want to do the calculation in native code so we declare the method as native in Java.

    public native void calculateMandelbrot(Bitmap b);

    This means the corresponding c function looks like this:

    static void calculateMandelbrot(JNIEnv *env, jobject thiz, jobject bitmap)

    We have a reference to the Java Bitmap object that we want to manipulate in our native code.

    In order to call the “setPixel” method on the Bitmap object in the c code we need to do a few things. Look at the following lines

    jclass bitmapClass = (*env)->GetObjectClass(env,bitmap);

    jmethodID bitmapSetPixel = (*env)->GetMethodID(env, bitmapClass,”setPixel”,”(III)V”);

    (*env)->CallVoidMethod(env, bitmap,bitmapSetPixel,x_index,y_index,colour);

    In order to get the function pointer to the desired java method (‘setPixel’) we first need to find out the jclass of our Bitmap object. We can then ask the JVM to “give us the method id for a method in the Bitmap class named ‘setPixel’ that takes three integer values as arguments and returns void”. Finally we use the “CallVoidMethod” to actually call the ‘setPixel’ method on the Bitmap object.

  8. RamSarvan says:

    Hi Sir,

    Could you please shed some light on this:
    It works both ways, that is you can use JNI to call native code from your Java programs and to call Java code from your native code.

    How to call java(android) code from your C code?

    Thanks.

  9. Mattias Rosberg says:

    Ok, I added some more information under “Build using Android NDK”. Hope this gives you an idea on how to proceed.

  10. Tom says:

    Great post — thanks for doing this. Would it be possible for you to add some more details for us Eclipse beginners about this: “The resulting libs folder should be included into your Android project?” A screenshot of what the Eclipse Package Explorer looks like once it is set up would be awesome. Thanks!

  11. Chanjin says:

    Thanks for your simple introduction of JNI & NDK.
    I think your next article will be about Android JIT mechanism.
    Boundary line between bytecode & JITted native code will be tricky without some guidelines or explicit direction by developer for which part can be JITted.
    Or, can all the bytecode be JITted?

  12. Mattias Rosberg says:

    It’s correct that you always have to compile your native code against a specific architecture. The Android NDK includes a cross-compiler (and linkers etc) that will generate binaries for the ARM architecture. Since ARM is by far the most common architecture on the Android devices today it should work on most devices (compatible with the Android SDK version 1.5 or higher). In a near future we will see Android devices running on Intel among others. This means that we need a new set of tools to be able to create binaries for these architectures. Hopefully, as part of Android NDK.

  13. Martin says:

    Hi, thanks for this blog, I’m a beginner java developer (and I have 10 years of c/c++). I have only one question: Is this native code will compatible with all processors? or how it works? When I’m working in C, I always compile a target processor, so I don’t know, my native stuff will work on all hardware.
    thanks for your help.

    • Bhanu sharma says:

      i developed one small hello world but frend now i have to implement some kind of lib like rsync for delta i have all code in c++ but my question is that like for all c++ code i have to edit JNI code for that classes like in jni folder because i use .dll file in java project it run fine but android not take .dll file so what i do for this…:(

Leave a Reply