When writing C/C++ code for Android application, it’s very common to use other native libraries rather than writing everything from scratch.
My first attempt was putting all library source directories under jni/
directory, and compiling them with a single make command with properly configured Android.mk files.
jni/
Android.mk
(project source files...)
libraryA/
Android.mk
(library A source files...)
libraryB/
Android.mk
(library B source files...)
With this structure, I wanted that jni/Android.mk
would first visit its sub directories, then jni/libraryA/Android.mk
and jni/libraryB/Android.mk
would compile their source codes into a shared/static module file, and then finally jni/Android.mk
compiles project’s source codes into a .so
file for Android Application.
Long story short, this can’t be.
Usually, 3rd party source codes cannot be compiled unless it is configured, and Android.mk
is not a good place to run configuration.
Most C/C++ libraries are cross-platform and require tools such as autoconf, automake, and libtool to dynamicaly generate Makefile and config.h according to the system environment.
This is usually done by running a simple command like this;
$ ./configure && make && make install
However, ndk-build
is basically a cross-compile, and that means a system that builds the library is not the same as a system that runs the compiled codes of the library. Therefore, running configure
and make
command is meaningless.
But without configuration, there would be no Makefile and build cannot be done. Now what?
In this case, configuration should be done manually.
Fortunately, well-kown libraries are distributed with a document that contains information on how to build the library for android, and Google also provides a guide on configuring toolchain
For example, libjpeg-turbo
gives a general recipe script for Android NDK in its BUILDING.md file.
Another problem with that structure is that build can easily get broken. Android.mk is not designed to work recursively. (Actually it doesn’t have to. It will be explained later)
New structure
My solution is to build each libraries into a .a
or .so
following their guide document, and move them into directories like this;
jni/
Android.mk
my_jni_code/
Android.mk
(project source code...)
libraryA/
Android.mk
liba.h
liba.a
libraryB/
Android.mk
libb.h
libb.so
Note that my_jni_code
directory is added, which contains source files written for the project, and library A and B are now at the same level with project file directory.
In this version, jni/Android.mk
contains only one line;
include $(call all-subdir-makefiles)
NDK build system will automatically detects all the dependencies and builds an NDK module.
And let’s assume that we want to link library A statically, and library B dynamically.
jni/libraryA/Android.mk
file would look like this;
LOCAL_PATH := $(call my-dir)
include ($CLEAR_VARS)
LOCAL_MODULE := library-a
LOCAL_SRC_FILES :=\
liba.a \
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/.
include $(PREBUILT_STATIC_LIBRARY)
liba.a
has been built with its build script for Android and copied into that place. In other words, library-a
is a prebuilt static library
And the following is what jni/libraryB/Android.mk
would look like;
LOCAL_PATH := $(call my-dir)
include ($CLEAR_VARS)
LOCAL_MODULE := library-b
LOCAL_SRC_FILES :=\
libb.so \
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/.
include $(PREBUILT_SHARED_LIBRARY)
Finally, to reference those libraries in Android NDK module, add these lines to jni/my_jni_code/Android.mk
LOCAL_STATIC_LIBRARIES := library-a
LOCAL_SHARED_LIBRARIES := library-b
Note that the module names that were assigned to LOCAL_MODULE
in their Andorid.mk file are used as reference, not file names like liba.a, libb.so, libraryA/liba.a nor libraryB/library-b. As mentioned above, NDK build system automatically detects module library-a
and library-b
, so without having to know where those libraries are located, my_jni_code
just can reference them by their module name. This is why Android.mk does not have to be written to work recursively.
Summary
When use 3rd party native library in Anroid NDK module, build them separately and use as prebuilt shared/static library. Don’t include library compilation into NDK module build.