NDK: Native Development Kit

Functions written in C or C++ are called native code. For example, we will create a native function named stringFromJNI that will return the string "Hello from JNI !" to the Java part of the app. The Java code will then display this return value in a white-on-black TextView. JNI stands for “Java Native Interface”.

Declare a native function as a method in a .java file with the Java keyword native.

	public native String stringFromJNI();
Define a native function in a .c or .cpp file in the jni folder of your project.

Source code in HelloJni.zip

  1. HelloJni.java
  2. hello-jni.c
  3. Android.mk
  4. AndroidManifest.xml

To open the above .zip file, see here. If Eclipse complains about Override in HelloJni.java, right-click the name of the project in the Package Explorer,
Android Tools → Fix Project Properties

Install the NDK

I downloaded the NDK into the /Users/myname/Downloads folder of my Mac, creating the folder /Users/myname/Downloads/android-ndk-r5b. Add the name of this folder to your PATH environment variable as in step 2 of the installation instructions. This folder contains the executable files ndk-build and ndk-gdb:

cd /Users/myname/Downloads/android-ndk-r5b
pwd
ls -l			lowercase LS minus L
ndk-build --help
ndk-gdb --help

The folder /Users/myname/Downloads/android-ndk-r5b also contains the NDK documentation. Start with OVERVIEW.html and INSTALL.html, which says that we need GNU make 3.81 or later (not to mention awk). Open a Mac Terminal window and say

which make
/usr/bin/make

make -v
GNU Make 3.81
This program built for i386-apple-darwin10.0

Windows people: the comments in ndk-build suggest you can use the make that comes with Cygwin.

The first sample application: hello-jni

When I installed the NDK, the sample application was installed into the folder /Users/myname/Downloads/android-ndk-r5b/samples/hello-jni.

cd /Users/myname/Downloads/android-ndk-r5b/samples/hello-jni
pwd

I see familiar subfolders such as src and res, and unfamiliar ones such as jni.

ls -l
-rw-r-----@ 1 myname  staff  695 Nov 12 18:53 AndroidManifest.xml
-rw-r-----@ 1 myname  staff  364 Nov 12 18:53 default.properties
drwxr-x---@ 4 myname  staff  136 Nov 12 18:53 jni
drwxr-x---@ 3 myname  staff  102 Nov 12 18:53 res
drwxr-x---@ 3 myname  staff  102 Nov 12 18:53 src
drwxr-x---@ 5 myname  staff  170 Nov 12 18:53 tests

The C file

The jni folder contains the file hello-jni.c, written in the language C. The header files string.h and jni.h are in the folder
/Users/myname/Downloads/android-ndk-r5b/platforms/android-9/arch-arm/usr/include.

The name of the C function Java_com_example_hellojni_HelloJni_stringFromJNI consists of three parts, separated by underscores. I’m afraid it’s right up there with the Maher-Shalal-Hash-Baz. in Isaiah 8:1.

  1. the package name com_example_hellojni
  2. the Java class name HelloJni
  3. the Java method name stringFromJNI

thiz is a pointer (of type jobject) to the HelloJni object in HelloJni.java. It has a z because the word this is a keyword in C++. If the HelloJni had any fields, we could have used thiz to access the fields. jni.h tells us that a jobject is a pointer to a very minimal object, which means it can point to any object: really means:

//Class _jobject has no members at all.
class _jobject {};

//A jobject is a pointer to a _jobject object.
typedef _jobject* jobject;

The return type of Java_com_example_hellojni_HelloJni_stringFromJNI is jstring, which is a bit more complicated:

//Class _jobject has no members at all.
class _jobject {};

//Class _jstring is derived from class _jobject.
class _jstring: public _jobject {};

//A jstring is a pointer to a _jstring object.
typedef _jstring *jstring;

The word JNIEnv is a pointer to a structure in C, a structure in C++.

Android.mk

The folder
/Users/myname/Downloads/android-ndk-r5b/samples/hello-jni/jni
also contains the “make” file Android.mk, which is read by the program ndk-build. my-dir is the current directory, i.e., the directory that holds Android.mk. CLEAR_VARS and BUILD_SHARED_LIBRARY are the names of make macros that hold the names of the makefiles clear-vars.mk and build-shared-library.mk in the directory /Users/nyuuser/Downloads/android-ndk-r5b/build/core.

ndk-build

Let’s run ndk-build.

cd /Users/myname/Downloads/android-ndk-r5b/samples/hello-jni
ndk-build

Gdbserver      : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver
Gdbsetup       : libs/armeabi/gdb.setup
Compile thumb  : hello-jni <= hello-jni.c
SharedLibrary  : libhello-jni.so
Install        : libhello-jni.so => libs/armeabi/libhello-jni.so

To remove the files you just created,

ndk-build clean

To create the same files again, this time with terrifying verbosity,

ndk-build -B V=1

What folders and files did we just create? With Unix `back quotes`, we can list all the folders and files starting at the current folder and working downwards, listing them from newest to oldest:

ls -ldt `find . -print` | more
drwxr-xr-x   5 myname  staff     170 May 11 11:35 ./libs/armeabi
-rwxr-xr-x   1 myname  staff    1588 May 11 11:35 ./libs/armeabi/libhello-jni.so
drwxr-xr-x   4 myname  staff     136 May 11 11:35 ./obj/local/armeabi
-rwxr-xr-x   1 myname  staff   21226 May 11 11:35 ./obj/local/armeabi/libhello-jni.so
drwxr-xr-x   4 myname  staff     136 May 11 11:35 ./obj/local/armeabi/objs-debug/hello-jni
-rw-r--r--   1 myname  staff   23316 May 11 11:35 ./obj/local/armeabi/objs-debug/hello-jni/hello-jni.o
-rw-r--r--   1 myname  staff    1138 May 11 11:35 ./obj/local/armeabi/objs-debug/hello-jni/hello-jni.o.d
drwxr-x---@ 10 myname  staff     340 May 11 11:35 .
drwxr-xr-x   3 myname  staff     102 May 11 11:35 ./libs
-rw-r--r--   1 myname  staff     318 May 11 11:35 ./libs/armeabi/gdb.setup
drwxr-xr-x   3 myname  staff     102 May 11 11:35 ./obj
drwxr-xr-x   3 myname  staff     102 May 11 11:35 ./obj/local
drwxr-xr-x   3 myname  staff     102 May 11 11:35 ./obj/local/armeabi/objs-debug

Examine the shared object file

To examine the library
/Users/myname/Downloads/android-ndk-r5b/samples/hello-jni/libs/armeabi/libhello-jni.so
that ndk-make just created,

cd /Users/myname/Downloads/android-ndk-r5b/samples/hello-jni/libs/armeabi
ls -l libhello-jni.so
file libhello-jni.so
libhello-jni.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, stripped

To see the contents of libhello-jni.so, I had to copy it to oit2.scps.nyu.edu and run the following command on oit2.scps.nyu.edu. I couldn’t get the Macintosh nm to display the contents of the file.

nm -D libhello-jni.so | awk 'NR == 3 || NR == 5 || /stringFromJNI/'
libhello-jni.so:
[Index]   Value      Size    Type  Bind  Other Shndx   Name
[49]    |      2961|      52|FUNC |GLOB |0    |7      |Java_com_example_hellojni_HelloJni_stringFromJNI

Launch Eclipse

File → New → Project… → Android → Android Project
Next
• Create project from existing source
Location: /Users/myname/Downloads/android-ndk-r5b/samples/hello-jni
Build Target: Android 2.3.3
Finish

▼ HelloJni
  ▼ src
    ▼ com.example.hellojni
      ► HelloJni.java

HelloJni.java

The Activity in HelloJni.java has a static initialization block that loads the C library hello-jni from the shared object file libhello-jni.so. Then the onCreate calls the C function stringFromJNI. I had to do a
Project → Clean…
to get it to compile.

adb shell
# cd /data/data/com.example.hellojni/lib
# pwd
# ls -l libhello-jni.so
# exit