Using NDK r4 with the Android SDK in Netbeans

This is a subject that's seldom talked about, but can be quite important. The NDK basically allows you to port any native C/C++ code/libraries into your Android project, using JNI (Java Native Interface).

I'm going to go through a quick and simple example of how you can display "Hello World" in your Android application using some native C.

The first thing we're going to do is download the NDK, which you can get by clicking on this link - http://developer.android.com/sdk/ndk/index.html - you can pick whichever one you need based on your OS.

I'm also going to assume that you already have the Android SDK installed and setup - for this example I'm also going to be using Netbeans, but you can easily use this on Eclipse as well.

Alright, so now that we're all ready, we can start a new Android project, and the main activity should look like this:


package org.xil3.nativeapp;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

/**
 *
 * @author jon
 */
public class NativeApp extends Activity {

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

    Log.v("NativeApp", "string from JNI: " + helloStringFromJNI());
  }

  public native String  helloStringFromJNI();

  static {
      System.loadLibrary("hello-jni");
  }
}

Now this is a pretty simple Activity, but the main differences are the uses of the native method and the static which loads the C library. By using the name native, you're telling it that you want to use a method within the C library and declare it for use in your Activity.

The second thing is the System.loadLibrary - this loads your C library.

This bit can be a bit tricky, so I'm going to talk a bit more about the pathing and where it looks for that library after I go through the compilation of the C source. First and foremost, the full name of that library is libhello-jni.so, but you can strip out the "lib" and ".so" - the system will still know what you're talking about, or you can just put in the whole thing - it won't matter.


// org_xil3_nativeapp_NativeApp.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class org_xil3_nativeapp_NativeApp */

#ifndef _Included_org_xil3_nativeapp_NativeApp
#define _Included_org_xil3_nativeapp_NativeApp
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     org_xil3_nativeapp_NativeApp
 * Method:    stringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_org_xil3_nativeapp_NativeApp_helloStringFromJNI
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

This bit of code is the C header - this was actually generated using a nice tool called javah. You can do the same by navigating to your java class folder (/project/build/classes/ - in Netbeans), and run the following:

javah -jni org.xil3.nativeapp.NativeApp

This should generate something similar to what I have above. Once that's generated, create a new directory under /project called JNI, and copy the newly generated C header there (the C header would of been generated somewhere under the classes directory.


// hello-jni.c

#include "org_xil3_nativeapp_NativeApp.h"

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

Now we create the C implementation file, which will also live under the JNI directory.


LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

And the last thing we need to add to the JNI directory is the Android.mk file, which tells it which files to compile. Not going to go into great detail about the .mk file, but you can read up on it by going into your /android_ndk_dir/docs/ANDROID-MK.TXT - that should explain it plenty.

We're all done now with the C coding! On to compiling the stuff…

To compile this we simply run the following (finally we're using the NDK that you downloaded earlier!):

/android_ndk_dir/ndk-build -C /project/location/

If the compilation is successful, it would have created a new directory under your project directory called "libs/armeabi/libhello-jni.so"

Okay, at this point we're pretty much done the main stuff, and normally you'd just be able to compile and it would work, but if you're using Netbeans (nbandroid plugin - kenai), it has some issues when it comes to adding the new C library to the final .apk build - in Eclipse this may work fine at this point, but since this is an article for Netbeans, I'll let you know what we need to do next.

Open up your /project directory, and look for build.xml - it should be there. Open that up, and we'll have to add something to it.


<target name="-post-jar">
  <zip update="true" destfile="${dist.apk}">
    <zipfileset dir="libs/armeabi" includes="*.so" prefix="lib/armeabi"/>
  </zip>
  <zip destfile="tmp.apk">
    <zipfileset src="${dist.apk}">
      <exclude name="META-INF/*.*" />
    </zipfileset>
  </zip>
  <move file="tmp.apk" tofile="${dist.apk}" />
  <signjar jar="${dist.apk}" alias="keystor_lias" storepass="store_password" keypass="key_password" keystore="/location/of/your/key.keystore"/>
</target>

After the import file tag, please add this bit of markup - this basically tells it to check in /libs/armeabi for any .so files and creates a lib/armeabi directory in the .apk and moves the .so files there, then finally re-compresses it all in the .apk.

Also, for signing the jar, if you don't have a keystore set up, you can use the debug one - the details are below:

Keystore name: "debug.keystore"
Keystore password: "android"
Key alias: "androiddebugkey"
Key password: "android"

Not 100% sure where the debug.keystore is located in Windows, but on my Mac and assuming in Linux it's under your home directory - ~/.android/debug.keystore

Hope this helps somebody!

jon | June 02, 2010 | Comments (1)

Comments

That’s really shwred! Good to see the logic set out so well.
Comment by Dora - May 07, 2011 @ 10:13 am

Name (required)
Email (will not be published) (required)
Website

captcha