How to use an external C++ library from native Android code

8,378 views
Skip to first unread message

Andreas

unread,
Jan 8, 2011, 3:53:44 AM1/8/11
to android-ndk
A summary of how I ported an existing C++ Open-Source library (Exiv2)
to Android using the Android SDK (android-8) and NDK r5 (on Mac OS X)
with the help of the Android NDK Dev Guide and various posts from this
and other forums. Maybe these notes will help others save a little
time. Comments are appreciated, this is my first NDK project and there
is always room for improvement.

The task can be broken down into three steps:


1. Cross-compile the Exiv2 library for Android

The details for this step are described in the section "Standalone
Toolchain" of the Android NDK Dev Guide.

NDK r5 comes with a toolchain with a recent gcc and full GNU STL
support so this step depends mainly on how portable the library
is. Exiv2 is built with the common GNU ./configure; make; make install
method. It makes extensive use of the STL and C++ exceptions, so
support for these is critical.

First I installed the NDK toolchain according to the NDK Dev Guide.
I used the following settings to configure and cross-compile Exiv2:

TARGET=arm-linux-androideabi
CC=$TARGET-gcc
CXX=$TARGET-g++
CFLAGS='-mthumb -O2'
CXXFLAGS='-mthumb -O2'
LDFLAGS='-Wl,--fix-cortex-a8'
LIBS='-lstdc++ -lsupc++'

./configure --host=$TARGET --prefix=$PROJECT/external/exiv2 --disable-
nls \
--disable-xmp --disable-shared
make -j3
make install

This builds only a static library and the --prefix setting causes it
to be installed in $PROJECT/external/exiv2/lib and the header files in
$PROJECT/external/exiv2/include/exiv2. The configure scripts of other
libraries may need different parameters, but --host and --prefix
should usually be supported.

The configure step for Exiv2 failed initially as it couldn't determine
the host system. I had to replace the config/config.guess and
config/config.sub files from the Exiv2 distribution with newer
versions from GNU Libtool (http://www.gnu.org/software/libtool/). With
these I was able to configure and build the library without further
changes.


2. Add the prebuilt library to the Android build system as a module

This and the next step use new features of the NDK r5 which are
described in sections "Prebuilts" and "Import Module" of the NDK Dev
Guide.

The following lines define an Android NDK module 'exiv2' for the
prebuilt Exiv2 library. This module can be imported from other NDK
Android.mk files. I saved it as $PROJECT/external/exiv2/Android.mk and
set the NDK_MODULE_PATH environment variable to $PROJECT/external:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := exiv2
LOCAL_SRC_FILES := lib/libexiv2.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_LDLIBS := -lz
include $(PREBUILT_STATIC_LIBRARY)

The line LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include ensures that
modules which use the exiv2 module can find its headers.
Also note the line with LOCAL_EXPORT_LDLIBS := -lz: Exiv2 depends on
zlib, i.e., an application that uses Exiv2 needs to link with zlib as
well. This line makes sure that any module which imports the exiv2
module will link with zlib automatically.


3. Use the Exiv2 module from an Android NDK project

Once it is defined, using the exiv2 module from another Android.mk
file is simple. As I'm new to the NDK myself, I started with the
hello-jni sample from the NDK and converted it to a C++ wrapper around
some Exiv2 calls. My final hello-jni/jni/Android.mk file looks like
this:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.cpp
LOCAL_STATIC_LIBRARIES := exiv2
include $(BUILD_SHARED_LIBRARY)
$(call import-module,exiv2)

This defines a hello-jni module which depends on the exiv2 module. The
last line imports the Android.mk file from the $NDK_MODULE_PATH/exiv2
directory and the line LOCAL_STATIC_LIBRARIES := exiv2 says that the
exiv2 module should be linked to the hello-jni module.

Exiv2 uses the C++ STL and exceptions which the default NDK build
system doesn't provide. An Application.mk file is required to select
the GNU STL and enable full C++ exception and RTTI support:

APP_CPPFLAGS += -fexceptions
APP_CPPFLAGS += -frtti
APP_STL := gnustl_static

Finally, my working hello-jni.cpp sample now looks something like
this:

#include <cstring>
#include <jni.h>
#include <string>
#include <sstream>
#include <exiv2/exiv2.hpp>

extern "C" {
JNIEXPORT jstring
JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv*
env, jobject thiz);
}

JNIEXPORT jstring
JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env,
jobject thiz)
{
std::ostringstream os;
try {
std::string file("/sdcard/download/img_2158.jpg");
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file);
// ...
}
}
catch (Exiv2::Error& e) {
// ...
}
return env->NewStringUTF(os.str().c_str());
}

Andreas Huggel

unread,
Jan 12, 2011, 9:32:28 AM1/12/11
to android-ndk
Exiv2 actually also depends on the expat library (for XMP support). So
I compiled a static expat library and created an expat module similar
to the exiv2 module.

How do I now modify the exiv2 module from the original post so that it
depends on the expat module in such a way that the hello-jni shared
library eventually links with expat? I'm looking for something similar
to the LOCAL_EXPORT_LDLIBS := -lz statement in the exiv2 module but
now for a dependency on a prebuilt static library for which I have
declared a module.

(I can compile fine with LOCAL_STATIC_LIBRARIES := exiv2 expat in the
hello-jni Android.mk, but that's not really nice as it requires the
hello-jni module to know about an exiv2 detail.)

Andreas

imudin

unread,
Apr 25, 2017, 2:38:21 PM4/25/17
to android-ndk
Hello, Andreas

Thank you very much for the great post.
This is exactly what I want to achieve.
I am new in cpp jni android. I am having hard time to set up and cofigure things.
It would be really nice if there is an example or git repo so that I can see and learn how to do.
Thanks in advance,
Mudin
Reply all
Reply to author
Forward
0 new messages