Today I’ll be posting a quick walkthrough of how to create and build a simple android project that includes native code using the Java Native Interface (JNI). As a note, there are sample projects included in the Android NDK, but this will walk you through building your own. After going through this, it’s suggested you review these sample projects.
Prerequisites
As a prerequisite for this tutorial, you’ll need:
- Eclipse installed and configured to create Android projects. There are a number of tutorials out there about how to do this if you need help.
- A JDK installed, as I don’t believe the standard JRE contains the javah command that will be needed.
- The Android NDK (available here) downloaded and extracted somewhere.
Create the Android Project
For this tutorial we’re going to create a new project. However there is no reason you couldn’t integrate this into an already-created project.
To create the project, right click in Eclipse’s Package Explorer → New → Android Project. Give your project a name and select an API. For this tutorial I choose the latest Gingerbread API, 2.3.3.
Add jni folder, Android.mk makefile
Once your project has been created, you’ll need to create a new folder inside the top level of the project. To do this right click on your project name → New → Folder. Name this folder jni.
Inside this folder, create a new blank text file. To do this right click on your newly-created jni folder → New → File. Name this file Android.mk. Leave this file blank for now, we’ll come back to it later.
Your project should look something like this:
Image may be NSFW.
Clik here to view.
Create java source
For this tutorial, we’re going to have a simple c program — squared — that accepts an int and returns the square (e.g. 2 → 4, 3 → 9, etc.)
In order to accomodate that, we first create a java source wrapper. The wrapper’s job is to load the library, expose any native functions we wish to use directly, and provide any functions that we want to be able to utilize private native functions.
For this tutorial, we’re going to expose directly the native squared function as well as provide a to4 “derivative” function.
To expose the native function directly, we just declare it public. Alternatively, we could declare it private and limit it’s availability to other functions of the class.
Our full java source is
package org.edwards_research.demo.jni; public class SquaredWrapper { // Declare native method (and make it public to expose it directly) public static native int squared(int base); // Provide additional functionality, that "extends" the native method public static int to4(int base) { int sq = squared(base); return squared(sq); } // Load library static { System.loadLibrary("squared"); } }
Create C header
After we outline the native methods we’ll be using, we can use this java source to create a c header file with the function prototypes for the native methods we used. To do this, we first have to compile the java source into a class file. You can do this manually via the javac command, e.g.:
cd src # change into the source directory javac -d /tmp/ org/edwards_research/demo/jni/SquaredWrapper.java
Note that the -d switch specifies the output directory for the class file — in this case, I’m just throwing it into /tmp.
Now that we have the class, we can create the c header file., e.g.:
cd /tmp javah -jni org.edwards_research.demo.jni.SquaredWrapper
Note the need to specify the fullly-qualified class name (including package) and not the .class file extension.
The resulting header file in our case is /tmp/org_edwards_research_demo_jni_SquaredWrapper.h, but we can rename it to whatever we want. In this case, we’ll rename it to squared.h and place it in the jni folder in our project directory.
The resulting squared.h file looks like:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class org_edwards_research_demo_jni_SquaredWrapper */ #ifndef _Included_org_edwards_research_demo_jni_SquaredWrapper #define _Included_org_edwards_research_demo_jni_SquaredWrapper #ifdef __cplusplus extern "C" { #endif /* * Class: org_edwards_research_demo_jni_SquaredWrapper * Method: squared * Signature: (I)I */ JNIEXPORT jint JNICALL Java_org_edwards_1research_demo_jni_SquaredWrapper_squared (JNIEnv *, jclass, jint); #ifdef __cplusplus } #endif #endif
The function name is annoying long, I agree. We could eliminate a lot of that if we didn’t use a package in our java source, but I’m not really that concerned.
Create C source
Using the prototype generated by javah, we can implement our c source as follows:
#include "squared.h" JNIEXPORT jint JNICALL Java_org_edwards_1research_demo_jni_SquaredWrapper_squared (JNIEnv * je, jclass jc, jint base) { return (base*base); }
Note we have to give the parameters names and I arbitrarily chose je andjc, and chose base to replicate our java source parameter name.
In this case, the c source is very simple, but this tutorial is meant to illustrate how to include native code into your Android app and more complex c functions could be substituted with few modifications.
Create Android.mk
After we create our c source file, we have to create our Android.mk file. This file serves the as a sort of makefile for the Android build tools. There are a number of sample Android.mk files in the samples/ directory of the NDK and we’ll actually be using almost the exact lines from the hello-jni sample project.
Our Android.mk file looks like:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := squared LOCAL_SRC_FILES := squared.c include $(BUILD_SHARED_LIBRARY)
At this point, we have laid most of our groundwork for setting up and compiling the library. The next steps are actually creating the shared library, and implementing some simple UI code to show that our native function (squared) and derivative function (to4) work as expected.
This is continued in Part 2.