Thursday 10 September 2009

How create a new JNI / STL based project for Android

Now that I've been through the process of creating new JNI / STL-based projects for Android a few times, I thought I'd summarise the steps; as you can see, they are pretty complicated! The problem isn't down to STL; the problem is one of the basic complexity of setting up a JNI-based project (which is never easy to get started, but is fine once you're up and running!).

Note: the steps below assume you're using cygwin and Windows. You'd need to tweak them a little if you're using Mac or Linux.

1. Make sure Eclipse uses a sensible workspace folder

NOTE!! I modified my Eclipse preferences to put my files in
Documents and Settings/myname/My Documents/workspace
rather than the Eclipse default of
Documents and Settings/myname/workspace
which I find really difficult to track-down and/or backup!

2. Create a new Android Java project.

a) Create a package called com.mycompany.MyStl, with your activity class called AdaptorClass

Ensure the following code is in your ActivityClass source code...


// A native method that is implemented by the
// 'libMyStl' native library, which is packaged
public native String stringFromSTL();

// Note: remember to put-in some test code that calls the above native method!!

// this will load the 'libMyStl.so' library on application startup.
static {
System.loadLibrary("MyStl");
}


b) you must add a "libs" folder to your project: and then add an "armeabi" sub-folder in this folder.

c) Build the project (and fix any compilation errors) - but don't run it yet!

3. Create the JNI / STL project

a) Create a folder in for your project, e.g.:


C:\android-ndk-1.5_r1\apps\MyStl


b) Create a file in this folder called Application.mk, which contains the following:


APP_PROJECT_PATH := /cygdrive/c/DOCUME~1/MYNAME/MYDOCU~1/WORKSP~1/MyStl
APP_MODULES := MyStl


NOTE!! The path is a path with no spaces (as otherwise the Android NDK make system gets confused...!), and was generated using cygwin's cygpath command. You must generate and use one that suits your user, using a command similar to the following...


cygpath -dm /cygdrive/c/Documents\ and\ Settings/myname/My\ Documents/workspace


c) Make an empty folder in this folder called "project".

4. Create the JNI / STL sources, and integrate with your Android project:

a) Create a folder for your C++ source code, e.g.:


C:\android-ndk-1.5_r1\sources\MyStl


b) Create a file in this folder called Android.mk ...


LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

STLPORT_BASE := /cygdrive/c/android-ndk-1.5_r1/stlport

LOCAL_MODULE := MyStl
LOCAL_CPP_EXTENSION := .cc
LOCAL_CFLAGS += -I$(STLPORT_BASE)/stlport \
-D__NEW__ \
-D__SGI_STL_INTERNAL_PAIR_H \
-DANDROID \
-DOS_ANDROID
LOCAL_SRC_FILES := MyStl.cc

LOCAL_STATIC_LIBRARIES := /../../../../../stlport/build/lib/obj/gcc/ar/libstlport.5.1

include $(BUILD_SHARED_LIBRARY)


c) Create a file in this folder called MyStl.cc ... note the instructions that follow in d) on how to re-generate your own method prototypes...!


/*
* Class: com_mycompany_MySTL_AdaptorClass
* Method: stringFromSTL
* Signature: ()Ljava/lang/String;
*/

// Prototype - make sure it uses C-style linkage!
extern "C"
{
JNIEXPORT jstring JNICALL Java_com_mycompany_MyStl_AdaptorClass_stringFromSTL
(JNIEnv *, jobject);
};

// Implementation
JNIEXPORT jstring JNICALL Java_com_mycompany_MyStl_AdaptorClass_stringFromSTL
(JNIEnv *env, jobject)
{
std::vector lVector;
lVector.push_back("Hello from STL!");

std::string simple_string = lVector[0];

return env->NewStringUTF(simple_string.c_str());
}


d) Modify the MyStl.cc file...

The prototype and implementation shown are just examples. You MUST construct your own by doing this:


cd "/cygdrive/c/Documents and Settings/myname/My Documents/workspace/MySTL/bin"
javah -jni com.mycompany.MySTL.AdaptorClass_MySTL


Copy-out the generated prototype from the generated .h file that javah creates for your, and replace what I've shown above with the correct generated function prototype and function name.

e) Then, build the library:


cd $NDK_HOME
./env.sh
make APP=MySTL


f) The first time you've built the library, you must drag the newly constructed .so file from Windows Explorer to your armeabi folder in the project. You must then right-click on the armeabi folder in Eclipse, and select "Refresh". The library is then bundled-in to your project bundle when you next build your Android project.

5. You can now build and run your Android project!

6. If you ever modify your C++ source...

Simply re-build the library (step 4e) and then re-build and run your Android project (step 5).

7. If you need to add a new method...

You'll need to re-use javah to generate the correct prototype function signature to use. Add this to your STL project, re-make, re-build and you're done!

No comments: