This recipe will discuss how to port an existing library as a shared
library with the Android NDK build system. We will use the open source libbmp
library as an example.
Readers are recommended to read the Building an Android NDK application at the command line recipe in Chapter 3, Build and Debug NDK Applications, before going through this one.
The following steps describe how to create our sample Android project that demonstrates porting the libbmp library as a shared library:
PortingShared
with native support. Set the package name as cookbook.chapter8.portingshared
. Please refer to the Loading native libraries and registering native methods recipe of Chapter 2, Java Native Interface, if you want more detailed instructions.MainActivity.java
under the cookbook.chapter8.portingshared
package. This Java file simply loads the shared library .bmp
and PortingShared
, and calls the native method naCreateABmp
.libbmp
library from http://code.google.com/p/libbmp/downloads/list, and extract the archive file. Create a folder named libbmp
under the jni
folder, and copy the src/bmpfile.c
and src/bmpfile.h
files from the extracted folder to the libbmp
folder.bmpfile.h
if you are using NDK r8 and below:#ifndef uint8_t typedef unsigned char uint8_t; #endif #ifndef uint16_t typedef unsigned short uint16_t; #endif #ifndef uint32_t typedef unsigned int uint32_t; #endif
#include <stdint.h>
The code changes for bmpfile.h
are only necessary for Android NDK r8 and below. Compiling the library will return an error "error: redefinition of typedef 'uint8_t'
". This is a bug in the NDK build system as the uint8_t
definition is enclosed by the #ifndef
preprocessor. It has been fixed since NDK r8b, and we
don't need to change the code if we're using r8b and above.
Android.mk
file under the libbmp
folder to compile libbmp
as a shared library libbmp.so
. The content of this Android.mk
file is as follows:LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libbmp LOCAL_SRC_FILES := bmpfile.c include $(BUILD_SHARED_LIBRARY)
libbmptest
under the jni
folder. Add the mylog.h
and PortingShared.c
files under it. PortingShared.c
implements the native method naCreateABmp
, which uses functions defined in the libbmp
library to create a bitmap image and save it to /sdcard/test_shared.bmp
. You will need to change directory if the /sdcard
directory is not available on your devices:void naCreateABmp(JNIEnv* env, jclass clazz, jint width, jint height, jint depth) { bmpfile_t *bmp; int i, j; rgb_pixel_t pixel = {128, 64, 0, 0}; for (i = 10, j = 10; j < height; ++i, ++j) { bmp_set_pixel(bmp, i, j, pixel); pixel.red++; pixel.green++; pixel.blue++; bmp_set_pixel(bmp, i + 1, j, pixel); bmp_set_pixel(bmp, i, j + 1, pixel); } bmp_save(bmp, "/sdcard/test_shared.bmp"); bmp_destroy(bmp); }
Android.mk
file under the libbmptest
folder to compile the PortingShared.c
file as another shared library libPortingShared.so
. The content of this Android.mk
file is as follows:LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := PortingShared LOCAL_C_INCLUDES := $(LOCAL_PATH)/../libbmp/ LOCAL_SRC_FILES := PortingShared.c LOCAL_SHARED_LIBRARIES := libbmp LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY)
Android.mk
file under the jni
folder with the following content:LOCAL_PATH := $(call my-dir) include $(call all-subdir-makefiles)
WRITE_EXTERNAL_STORAGE
permission to the AndroidManifest.xml
file as follows:<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
test_shared.bmp
should be created at the sdcard
folder of the Android device. We can use the following command to get the file:$ adb pull /sdcard/test_shared.bmp .
The following is a .bmp
file:
The sample project demonstrates how to port the libbmp
code as a shared library and use it in the native code PortingShared.c
.
Shared library: A shared library can be shared by multiple executables and libraries. The Android native code is usually compiled as shared libraries and loaded by the Java code. In fact, the Android build system only packages shared libraries into the application's apk
file. Therefore, we must provide at least one shared library to
contain our native code.
Our sample project builds two shared libraries, namely libbmp.so
and libPortingShared.so
. We can find these libraries under the libs
folder of the project. libPortingShared.so
depends on libbmp.so
, since PortingShared.c
calls functions defined in the libbmp
library.
In our Java file, we need to load libbmp.so
before libPortingShared.so
, as follows:
static { System.loadLibrary("bmp"); System.loadLibrary("PortingShared"); }
Understand the Android.mk files: Android NDK provides an easy-to-use build system, which frees us from writing makefiles. However, we still need to provide some basic inputs to the system through Android.mk
and Application.mk
. We only discuss Android.mk
in this recipe.
The Android.mk
file is a GNU makefile fragment that describes the sources to the Android build system. The sources are grouped into modules. Each module is a static or shared library. The Android NDK provides a few predefined variables and macros. Here, we will briefly describe the ones used in this recipe. We will introduce more predefined variables and macros in subsequent recipes and you can also refer to Android NDK docs/ANDROID-MK.html
for more information.
CLEAR_VARS
: This variable points to a script, which undefines nearly all module description variables except LOCAL_PATH
. We must include it before every new module, as follows:include $(CLEAR_VARS)
BUILD_SHARED_LIBRARY
: This variable points to a build script, which determines how to build a shared library from the sources listed, based on the module description. We must have LOCAL_MODULE
and LOCAL_SRC_FILES
defined when including this variable, as follows:include $(BUILD_SHARED_LIBRARY)
Including it will generate a shared library lib$(LOCAL_MODULE).so
.
my-dir
: This must be evaluated by using $(call <macro>)
. The my-dir
macro returns the path of the last included makefile, which is usually the directory containing the current Android.mk
file. It is typically used to define the LOCAL_PATH
, as follows:LOCAL_PATH := $(call my-dir)
all-subdir-makefiles
: This macro returns a list of Android.mk
files located in all subdirectories of the current my-dir
path. In our example, we used this macro in the Android.mk
file under the jni
, as follows:include $(call all-subdir-makefiles)
This will include the two Android.mk
files under libbmp
and libbmptest
.
LOCAL_PATH
: This is a module description variable, which is used to locate the path to the sources. It is usually used with the my-dir
macro, as follows:LOCAL_PATH := $(call my-dir)
LOCAL_MODULE
: This is a module description variable, which defines the name of our module. Note that it must be unique among all module names and must not contain any space.LOCAL_SRC_FILES
: This is a module description variable, which lists out the sources used to build the module. Note that the sources should be relative to LOCAL_PATH
.LOCAL_C_INCLUDES
: This is an optional module description variable, which provides a list of the paths that will be appended to the include search path at compilation. The paths should be relative to the NDK root directory. In Android.mk
, under the libbmptest
folder of our sample project, we used this variable as follows:LOCAL_C_INCLUDES := $(LOCAL_PATH)/../libbmp/
LOCAL_SHARED_LIBRARIES
: This is an optional module description variable, which provides a list of the shared libraries the current module depends on. In Android.mk
, under the libbmptest
folder of our sample project, we used this variable to include the libbmp.so
shared library:LOCAL_SHARED_LIBRARIES := libbmp
LOCAL_LDLIBS
: This is an optional module description variable, which provides a list of linker flags. It is useful to pass the system libraries with the -l
prefix. In our sample project, we used it to link the system log library:LOCAL_LDLIBS := -llog
With the preceding description, it is now fairly easy to understand the three Android.mk
files used in our sample project. Android.mk
under jni
simply includes another two Android.mk
files. Android.mk
under the libbmp
folder compiles the libbmp
sources as a shared library libbmp.so
, and Android.mk
under the libbmptest
folder compiles PortingShared.c
as the libPortingShared.so
shared library, which depends upon the libbmp.so
library.
It is possible to use a shared library in the native code, as we have demonstrated in the Programming with dynamic linker library at Android NDK recipe in Chapter 6, Other Android NDK API.