Search This Blog

Tuesday, August 28, 2012

NDK in Android

The Android Native Development Kit (NDK) helps you boost an app’s performance by converting C/C++ source code (in which you write the app’s performance-critical sections) to native code libraries that run on Android devices. The NDK provides headers and libraries for building activities, handling user input, using hardware sensors, and more.

Native code is often best used with processor intensive apps, but only where performance profiling has revealed a bottleneck that could be solved by recoding that portion of the app in native code.

In this blog I am going to share how to make JNI based Application in Android

The Following are prerequisite before going to develop an NDK Application
1. C/C++ Development Tools
2. Sequoyah Plugin
3.  Installing Cygwin
4. Android-NDK

Installing C/C++ Development Tools

  To install C/C++ Development tools in eclipse IDE choose Help->InstallNewSoftware...  menu item and select update site ( since I am using helios my CDT link will be http://download.eclipse.org/tools/cdt/releases/helios) select every C/C++ Development tools and follow on-screen instructions then click on restart prompt to make eclipse to update with CDT.


Installing Sequoyah Plugin

Sequoyah Plugin will enable a large range of changes into an Eclipse Android SDK project to give simultaneous support to the NDK. To install this plugin choose Help->InstallNewSoftware...  menu item and add the following site http://download.eclipse.org/sequoyah/updates/2.0/ and follow on-screen instructions to enable sequoyah plugin in eclipse.

 Installing Cygwin

Cygwin is a set of software that stimulates unix environment on windows. Download setup.exe from Cygwin setup at time of running this file a huge list appears where you can select the components to download. Search for “make” and “shell”and press next to download. After installing cygwin open it and type make-v which confirms cygwin is installed properly.

Downloading Android NDK 

 Download Android NDK form Android Official Site
Note: The NDK version used in below sample is from android-ndk-r7b

Developing Sample Application

In the below sample I tried to handle exception, return values from JNI and using log util of android SDK. So go into code the JNI module add the below code to return data from JNI to your Activity and do log action. Create a Project named NDKSample and create a folder named jni and create file name MyNativeCode.c and add the below module

#include <jni.h>
#include <string.h>
#include <android/log.h>
#include <stdio.h>

JNIEXPORT jstring JNICALL Java_org_ndk_sample_MyNativeCode_nativeCall
  (JNIEnv * env, jobject obj,jstring str)
{
    __android_log_print(ANDROID_LOG_INFO,
            "SampleJNI",
            " FIRST NDK METHOD CALL");
    return str;
}
In the above snippet explains how to define a native method JNIEXPORT means the method is exported as JNI function followed by return type and JNICALL means the class which is going to access the native method.
Note: The name of the Native Java class should be same as Native C file

To access this method we now define the Native Java class as shown below
public class MyNativeCode {
    public native String nativeCall(String data);
   }
 So to access the same from your activity create a object to this native class and call this method. Before that we need to compile the Native C to get the code as library, So we need to compile it using cygwin.

Create a make file under jni folder and add the following snippets into it

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := firstndk
LOCAL_SRC_FILES := MyNativeCode.c
LOCAL_LDLIBS    := -llog
include $(BUILD_SHARED_LIBRARY)
Here I am adding an external library called -llog which means to perform log handling in native code need to add this external library file. LOCAL_MODULE holds the name to used to call this library file from application. To know more on make file tags refer Make File tags

After this step follow the below screen to get compilation of native C.








In case of Linux there is no need of cygwin and the compilation can done as shown in screen shot below



At the end of this step library file is generated at libs folder of your project directory.

To call the library from following create Activity and add below snippet
static {
        System.loadLibrary("firstndk");
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        MyNativeCode nativeCode = new MyNativeCode();
        String str = nativeCode.nativeCall("Hello Android JNI");
        Log.d("Test", str);
}
When we run this application we can notice the string Hello Android JNI is logged with two different tags namely Test and SampleJNI.

Now to handle exception I defined the method in Native C which converts String to Hex So refer below snippet for the same
JNIEXPORT jstring JNICALL Java_org_ndk_sample_MyNativeCode_string2Hex(JNIEnv *env, jobject obj, jstring str) {

    if (str==NULL) {
        (*env)->ThrowNew(env, (*env)->FindClass(env,"java/lang/Exception"), "thrown from C code");
    } else {
        const char* data= (*env)->GetStringUTFChars(env,str,NULL);
            const char *retval=convert2Hex(data);
                return (*env)->NewStringUTF(env,retval);
    }
}
 const char* convert2Hex(const char *val) {
    int len = strlen(val);
    char *tmp=(char *)malloc(len*2);
    int i = 0;
    while(*val) {
            sprintf(&tmp[i*2],"%2X", *val++);
            i++;
    }
    return (const char*)tmp;
}
 and in Native java class we declare the above method as native function as shown below
public native String string2Hex(String str) throws Exception;
and in our activity class calling the native method as shown below
try {
            retval = nativeCode.string2Hex("Testing JNI");
            Toast.makeText(getApplicationContext(),
                    "HEX value for Testing JNI is ->>> " + retval, Toast.LENGTH_LONG)
                    .show();
            nativeCode.string2Hex(null);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            Toast.makeText(getApplicationContext(), e.getMessage(),Toast.LENGTH_LONG).show();
        }
Since we made changes in Native C we need to compile the code once again as specified above to reflect it in our activity and once done we will get an toast message thrown from C Code.

So this makes us to learn what is JNI, NDK and how to use it from application prespective