How to use 3rd party native libraries in Android project

Reading time ~3 minutes

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.

kiwi campus

가족여행차 샌 프란시스코에 갔다가 오랜만에 버클리 교정을 구경하러 갔다.돌아다니다보니 Sather gate 근처의 분수대 주변에 이런 게 돌아다니고 있었다.처음엔 학생이 만든 장난감 정도로 생각했다. 폰에 외장(?)렌즈를 달아서 꽂아놨는데 이게 ...… Continue reading