Once you start working with some of the handy new C++11 features, it is a bit difficult to stop using them simply because you want to work on an Android project. Fortunately, the Android NDK supports C++11 features, although they are not enabled by default. The default Android NDK configuration provides only a minimal C++ runtime support library and doesn't include the necessary flags to activate available C++11 features. In this article, I explain how set up a project to use C++11 features, relying on Eclipse and the latest available Android NDK, version r9d.
Consider a very common scenario in which you want to create a new Android app by reusing existing C++ code. In these cases, a typical solution is to develop the UI in Java and use the Java Native Interface (JNI) to make calls to the C++ code from Java (and the other way around if necessary). If your existing C++ code has been written taking advantage of C++11 features, you certainly would not want to create a new version removing all these C++11 features just to make it fit with the default Android NDK configuration. Fortunately, you can activate the NDK's C++11 features to allow you to work with modern C++ amenities, and you can go on using the auto
keyword, , and other useful C++11 features.
I'll assume that you have basic experience working with Eclipse, Android Development Tools (ADT), and the Android NDK; hence, I won't provide specific instructions for the basic setup of these tools. You will need ADT and NDK installed on your system in order to test the examples. Because ADT and NDK have important changes in each new release, it's important to note that I am using ADT Build v22.6.2-1085508 and NDK Revision 9d. This way, I can focus on the necessary configurations and tweaks related to C++11 and the related features. I'll use a simple example Android app that employs a few C++11 features combined with some use of the Standard Templates Library (STL). Finally, I'll explain additional options and configurations that you might need to consider.
Example Project
Create a new project in Eclipse and select the Android Application Project wizard. I use Cplusplus11
for both Application Name and Project Name, and the package name is com.example
. I leave the default options for the different steps within the wizard, and I select the Blank Activity option with the Create Activity checkbox activated. I use the default activity name, MainActivity
. This way, the wizard will create a Cplusplus11/src/com.example.cplusplus11/MainActivity.java file that defines the MainActivity
Java class as a subclass of ActionBarActivity
. I'll go back to the Java code later.
Now, it is necessary to add native support to the project. Right-click on the recently created project in the Project Explorer and select Android Tools | Add Native Support…. I use libCplusplus11.so
as the library name. Notice that you simply need to enter cplusplus11
in the dialog box and the Eclipse plugin will add the lib
prefix. The Eclipse plugin will add a new jni
folder to the project with the following two elements (see Figure 1): cplusplus11.cpp and Android.mk
Right-click on the project in the Project Explorer again, and select Properties | C/C++ Build. Deactivate the Use Default Build Command checkbox and enter ndk-build NDK_DEBUG=1
in the Build Command textbox. This way, the NDK debugger is enabled.
The default contents for the Cplusplus11.cpp
C++ source file is just a line that includes the jni.h
header:
1
|
#include <jni.h>
|
The default contents for the Android.mk
build file includes the following lines:
1
2
3
4
5
6
7
8
|
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := cplusplus11
LOCAL_SRC_FILES := cplusplus11.cpp
include $(BUILD_SHARED_LIBRARY)
|
It is often helpful to use the Android logging features when you call C++ code from Java through the JNI, so I add the following line to pass the name of the specific system library that provides Android logging features and tell the linker to generate a module that links to /system/lib/liblog.so
at load time. Notice that -llog
is decomposed in the -l
option followed by log
; that is, the library name with neither the lib
prefix nor the .so
extension.
1
|
LOCAL_LDLIBS := -llog
|
The following lines show the new contents of the Android.mk
build file:
1
2
3
4
5
6
7
8
9
10
11
|
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL
_MODULE := Cplusplus11
LOCAL_SRC_FILES := Cplusplus11.cpp
# Generate a module that links to /system/lib/liblog.so at load time to enable logging
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
|
Now, it is necessary to add a new build file to the jni
folder named Application.mk
. This file describes which native modules the Android application requires. The following lines show the initial content that I use for this build file:
1
2
3
4
5
6
7
|
NDK_TOOLCHAIN_VERSION := 4.8
APP_ABI := armeabi armeabi-v7a x86 mips
# Enable C++11. However, pthread, rtti and exceptions aren’t enabled
APP_CPPFLAGS += -std=c++11
# Instruct to use the static GNU STL implementation
APP_STL := gnustl_static
LOCAL_C_INCLUDES += ${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.8/include
|
The default compiler is GCC 4.6. The NDK_TOOLCHAIN_VERSION
key specifies the 4.8
value to define the toolchain to use GCC 4.8 as the compiler. The default configuration of the NDK build system generates the machine code for the armeabi
ABI; that is, the ARMv5TE-
based CPU. The APP_ABI
value allows you to select different ABIs.
If you are developing on Windows and you meet the necessary hardware requirements, it is usually a good idea to install the Intel x86 Emulator Accelerator (HAXM), which speeds up Android emulation on Windows. If you are working with this accelerated emulator, the CPU/ABI of your target Android Virtual Device (AVD) will be Intel Atom (x86)
, so you will need to include x86
in the APP_ABI
values. In this example, I've included all the target ABIs in APP_ABI
, but you can specify only the ones you need. Notice that you can achieve the same effect by using APP_ABI := all
. There are many additional options for APP_ABI
, but I wanted to focus on the importance of this setting because when you use different CPU/ABI configurations in your target AVD, the Java code will raise an exception if you try the load the library and you didn't specify the right values for APP_ABI
(since the appropriate library won't be generated).
The APP_CPPFLAGS
key specifies the set of C++ compiler flags passed when building C++ source. The following line enables the supported C++11 features for all your modules. However, you must take into account that pthread, RTTI, and exceptions aren't enabled by default. The C++11 support includes the auto
keyword and lambda expressions. There is also support for std::thread
, but note that there are still some problems with the usage of std::thread
that lead to unexpected app crashes.
1
|
APP_CPPFLAGS += -std=c++11
|
As I explained before, I want to use some STL features, so the following line specifies the static GNU STL implementation. There are other alternatives, such as the STLport library and the experimental libc++ implementation introduced in NDK r9d. You can read more information about libc++ .
1
|
APP_STL := gnustl_static
|
Finally, it is necessary to specify the path for the GNU STL implementation headers. The following line specifies the path to these headers included in the NDK folders. Because I've specified that the toolchain use GCC 4.8, I make sure the headers path corresponds to version 4.8. Notice that you must have the Android NDK path configured in Eclipse. To do this, select Window | Preferences | Android | NDK, and make sure that the NDK location value is properly configured.
1
|
LOCAL_C_INCLUDES += ${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.8/include
|