The Android platform provides a C++ runtime library at /system/lib/libstdc++.so
. This default runtime library does not provide C++ exception and RTTI. The support for a standard C++ library is also limited. Fortunately,
Android NDK provides alternatives to the default C++ runtime library, which makes porting of a large number of existing libraries that require exception, RTTI, and STL support, possible. This recipe discusses how to port a C++ library that requires RTTI, exception, and STL support. You will widely use the boost
library as an example.
The following steps describe how to build and use the boost
library
for Android NDK:
$ANDROID_NDK/build/tools/make-standalone-toolchain.sh --platform=android-9 --install-dir=/tmp/my-android-toolchain
This should install the toolchain at the /tmp/my-android-toolchain
folder.
PortingBoost
with native support. Set the package name as cookbook.chapter8.portingboost
. Please refer to the Loading native libraries and registering native methods recipe of Chapter 2, Java Native Interface for more detailed instructions.MainActivity.java
under the cookbook.chapter8.portingboost
package. This Java file simply loads the shared library PortingBoost
, and calls the native method naExtractSubject
.boost
library 1.51.0. Extract the downloaded archive file to the jni
folder. This will create a folder named boost_1_51_0
under the jni
folder as follows:boost_1_51_0
directory. Enter the following command:$ ./bootstrap.sh
user-config.jam
file under the jni/boost_1_51_0/tools/build/v2
folder. Append the following content to the end of the file. You can refer to http://www.boost.org/boost-build2/doc/html/bbv2/overview/configuration.html for more information about boost configuration:NDK_TOOLCHAIN = /tmp/my-android-toolchain ; using gcc : android4.6 : $(NDK_TOOLCHAIN)/bin/arm-linux-androideabi-g++ : <archiver>$(NDK_TOOLCHAIN)/bin/arm-linux-androideabi-ar <ranlib>$(NDK_TOOLCHAIN)/bin/arm-linux-androideabi-ranlib <compileflags>--sysroot=$(NDK_TOOLCHAIN)/sysroot <compileflags>-I$(NDK_TOOLCHAIN)/arm-linux-androideabi/include/c++/4.6 <compileflags>-I$(NDK_TOOLCHAIN)/arm-linux-androideabi/include/c++/4.6/arm-linux-androideabi <compileflags>-DNDEBUG <compileflags>-D__GLIBC__ <compileflags>-DBOOST_FILESYSTEM_VERSION=3 <compileflags>-lstdc++ <compileflags>-mthumb <compileflags>-fno-strict-aliasing <compileflags>-O2 ;
boost
library with the following command:$ ./b2 --without-python --without-mpi toolset=gcc-android4.6 link=static runtime-link=static target-os=linux --stagedir=android > log.txt &
This command will execute the boost
build
in the background. You can
monitor the build output by using the following command:
$ tail -f log.txt
The build will take some time to finish. It will fail to build some targets. We can examine the errors in the log.txt
file.
The first error is that the sys/statvfs.h
file is not found. You can fix this by updating the libs/filesystem/src/operations.cpp
file. The updated parts are highlighted as follows:
# include <sys/types.h> # include <sys/stat.h> # if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__ANDROID__) # include <sys/statvfs.h> # define BOOST_STATVFS statvfs # define BOOST_STATVFS_F_FRSIZE vfs.f_frsize # else # ifdef __OpenBSD__ # include <sys/param.h> # elif defined(__ANDROID__) # include <sys/vfs.h> # endif # include <sys/mount.h> # define BOOST_STATVFS statfs # define BOOST_STATVFS_F_FRSIZE static_cast<boost::uintmax_t>(vfs.f_bsize) # endif
The second error is that the bzlib.h
file is not found. This is because bzip
is available on Android. You can disable bzip
by adding the following line at the top of jni/boost_1_51_0/tools/build/v2/user-config.jam
:
modules.poke : NO_BZIP2 : 1 ;
The third error is that PAGE_SIZE
is not declared in
this scope. You can fix this by adding the following line to boost_1_51_0/boost/thread/thread.hpp
and boost_1_51_0/boost/thread/pthread/thread_data.hpp
:
#define PAGE_SIZE sysconf(_SC_PAGESIZE)
mylog.h
and PortingBoost.cpp
files under the jni
folder. The PortingBoost.cpp
file contains the implementation for the native method naExtractSubject
. The function will match each line of the input string pInputStr
with a regular expression using the boost
library's regex_match
method:void naExtractSubject(JNIEnv* pEnv, jclass clazz, jstring pInputStr) { std::string line; boost::regex pat( "^Subject: (Re: |Aw: )*(.*)" ); const char *str; str = pEnv->GetStringUTFChars(pInputStr, NULL); std::stringstream stream; stream << str; while (1) { std::getline(stream, line); LOGI(1, "%s", line.c_str()); if (!stream.good()) { break; } boost::smatch matches; if (boost::regex_match(line, matches, pat)) { LOGI(1, "matched: %s", matches[0].str().c_str()); } else { LOGI(1, "not matched"); } } }
Android.mk
file under the jni
folder with the following content:LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_regex LOCAL_SRC_FILES := boost_1_51_0/android/lib/libboost_regex.a LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/boost_1_51_0 include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := PortingBoost LOCAL_SRC_FILES := PortingBoost.cpp LOCAL_LDLIBS := -llog LOCAL_STATIC_LIBRARIES := boost_regex include $(BUILD_SHARED_LIBRARY)
Application.mk
file under the jni
folder with the following content:APP_STL := gnustl_static APP_CPPFLAGS := -fexceptions
$ adb logcat -v time PortingBoost:I *:S
The following is a screenshot of the logcat output:
In your sample project, you first built the boost library using the Android toolchain as a standalone compiler. You then used the regex
library from boost
as a prebuilt module. Note that the boost
library requires support for C++ exceptions and STL. Let's discuss how to enable support for these features at Android NDK.
C++ runtime at Android NDK: By default, Android comes with a minimal C++ runtime library at /system/lib/libstdc++.so
. The library does not support most C++ standard library functions,
C++ exceptions, and RTTI. Fortunately, Android NDK comes with additional C++ runtime libraries that we can use. The
following table summarizes the features provided by different runtime libraries at NDK r8:
C++ standard library |
C++ exceptions |
C++ RTTI | |
---|---|---|---|
system |
minimal |
No |
No |
gabi++ |
No (yes if NDK r8d or later) |
Yes | |
stlport |
No (yes if NDK r8d or later) |
Yes | |
gnustl |
yes |
Yes |
The system library refers to the default value that comes with the Android system. There is only a minimal C++ standard library support, and no C++ exceptions and RTTI. The C++ headers supported include the following:
cassert, cctype, cerrno, cfloat, climits, cmath, csetjmp, csignal, cstddef, cstdint, cstdio, cstdlib, cstring, ctime, cwchar, new, stl_pair.h, typeinfo, utility
gabi++
is a runtime library, which supports RTTI in addition to the C++ functions provided by the system default.stlport
provides a complete set of C++ standard library headers and RTTI. However, C++ exception is not supported. In fact, Android NDK stlport
is based on gabi++
.gnustl
is the GNU standard C++ library. It comes with a complete set of C++ headers, and supports C++ exceptions and RTTI.The Android NDK build system allows us to specify the C++ library runtime to link to the Application.mk
file. Based on the library type (shared or static) and which runtime to use, we can define APP_STL
as follows:
Static library |
Shared library | |
---|---|---|
gabi++ |
|
|
stlport |
|
|
gnustl |
|
|
In your sample project, add the following line in Application.mk
to use the gnustl
static library:
APP_STL := gnustl_static
You can only link a static C++ library into a single shared library. If a project uses multiple shared libraries and all libraries link to a static C++ library, each shared library will include a copy of the library's code in its binary. This will cause some problems, because some global variables used by the C++ runtime library are duplicated.
The sources, headers, and binaries of these libraries can be found at the sources/cxx-stl
folder of Android NDK. You can also refer to docs/CPLUSPLUS-SUPPORT.html
for more information.
Enable the C++ exception support: By default, all C++ sources are compiled with -fno-exceptions
. In order to enable C++ exceptions, you will need to choose a C++ library, which supports exceptions (gnustl_static
or gnustl_shared
), and do one of the following:
Android.mk
, add exceptions to LOCAL_CPP_FEATURES
as follows:LOCAL_CPP_FEATURES += exceptions
Android.mk
, add -fexceptions
to LOCAL_CPPFLAGS
as follows:LOCAL_CPPFLAGS += -fexceptions
Application.mk
, add the following line:APP_CPPFLAGS += -fexceptions
Enable the C++ RTTI support: By default, C++ sources are compiled with -fno-rtti
. In order to enable the RTTI support, you will need to use a C++ library, which supports RTTI, and do one of the following:
Android.mk
, add rtti
to LOCAL_CPP_FEATURES
as follows:LOCAL_CPP_FEATURES += rtti
Android.mk
, add -frtti
to LOCAL_CPPFLAGS
as follows:LOCAL_CPPFLAGS += -frtti
Application.mk
, add -frtti
to APP_CPPFLAGS
as follows:APP_CPPFLAGS += -frtti