Adding shared libraries to your Android NDK Build: Insanity

Stressful Guy

The Android NDK build system may make you want to choke something. (source: the eating disorder blog)

I’ve been pulling my hair out for the past couple days dealing with Android NDK build sensitivities.  I hope this blog posting saves you some time and stress. If it does, you can send me flowers …. or money.  Or both.  It’s all good.

So, let’s say you have an existing Android NDK project and it works file.  It’s complete.  You want to add a shared library into the application, but it’s not required to build your application. That is, you need it during runtime, but you don’t need it to build your C/C++ code using NDK.

It should be simple, right, but the documentation for the Android NDK seems to suggest that you have to use LOCAL_MODULE and friends to accomplish this.  But you’ll find that can be recipe for disaster.  I sure did.   It’s been tormenting me for the past several days and I think I finally solved the problem.

This blog presents one way to accomplish this.  This is something I just discovered (by spending hours ruling out everything that didn’t work – modifying module definitions in Android.mk files, using APP_MODULES, and setting up module dependencies – you name it, I’ve tried it) which I have not seen explicitly discussed about in the existing android documentation.

The Goal

The goal is simple. You have an existing NDK project and you want to:

  1. add a shared library to your Android NDK build that gets packed up into your .apk file. The library is one that is not needed to compile any other C/C++ module in your project. But it’s one that your application will rely upon indirectly.  You just need it to be packed with the build.
  2. Load that library at runtime with a call to
System.loadLibrary("foo-user");

The Problem

If you read the documentation, you’re going to realize two things: a) the Android NDK build system is built on top of gmake, and b) it relies very heavy on makefile fragments. You’ll see things like this, from PREBUILTS.html (from the Android NDK docs directory):

II. Referencing the prebuilt library in other modules:
------------------------------------------------------

Simply list your prebuilt module's name in the 
LOCAL_STATIC_LIBRARIES or LOCAL_SHARED_LIBRARIES declaration 
in the Android.mk of any module that depends on them.

For example, a naive example of a module using libfoo.so would be:

    include $(CLEAR_VARS)
    LOCAL_MODULE := foo-user
    LOCAL_SRC_FILES := foo-user.c
    LOCAL_SHARED_LIBRARIES := foo-prebuilt
    include $(BUILD_SHARED_LIBRARY)

You might be thinking that your shared library will just be pulled into the build.  But then when you run the build you suddenly get linking errors for your application that was working properly before.

You find that not only is your build failing due to linking errors, your shared library is not getting incorporated into the .apk file.  That’s what happened in my case.

Here’s Something That Works

I was able to accomplish this without build errors, after several days of fighting with the build, by using the following approach:

1. Do NOT add a module to your Android.mk make files.  Leave that out.  Apparently (and this is just a guess), you only should define prebuilt shared modules in your build file that your native application actually needs to be linked against.

2. Build your shared library in its own project.  Make sure the code is cross-compiled for the platform of Android you want the library to run on.  At this point you have two separate, independent projects: Your Android project with native code, and your shared library somewhere on the filesystem.

3. With your native project built, you’re going to see a libs directory at the top level of your project.  In that directory, you’ll see something like this:

MyProject/
     libs/
         armeabi-v7a/
             libfromyourndkproject.so

4. Copy your shared library into the directory that has the name of the architecture you’ve compiled it for.  So, if you’ve built libfoo-user.so for armeabi, you’ll want something like this;

MyProject
     libs
         armeabi-v7a
            libfromyourndkproject.so
            libfoo-user.so

With that setup you should see your shared library as part of your apk build file.  You can use

jar xvf YourApplicaton.apk

to verify it’s there.

There should be no mysteries, unanswered questions, or undocumented use cases when it comes to something as critical as a build system. Without that, your project can come to an untimely screeching halt.

Advertisements

2 thoughts on “Adding shared libraries to your Android NDK Build: Insanity

  1. What if want to talk to shared library with C++ code.

    For ex:

    1. I have a shared library (C++ based) which declares a class with function.

    class foo
    {
    const char* giveMeAString();
    }

    #include “foo.h”

    const char* foo::giveMeAString()
    {
    return “Hello World!”;
    }

    Now I have compiled foo class successfully in shared library called: foo.so

    2. Now suppose I am writing another project which is also having some CPP code. But this new CPP code wants to reuse the static library.

    // Declaration
    class fooMate
    {
    void printDemo();
    }

    // Implementation
    #include “fooMate.h”
    #include “foo.h”

    void fooMate::printDemo()
    {
    foo *testFoo = new foo();
    cout<giveMeAString();
    }

    Question: How can I achieve this by using foo.so file instead of using actual source code of foo.cpp

    • Hi Paras. I usually watch for these kinds of requests for help on the Android NDK Google Group at https://groups.google.com/forum/?fromgroups#!forum/android-ndk, or on slashdot.com.

      So, this reply is kind of late, but I hope it still helps.

      I can’t tell you exactly what you’re going to put into your Android project, but it’s going to go something like this:

      1. declare a module in an Android.mk file for the first shared library (class foo). You’ll use LOCAL_EXPORT_C_INCLUDES to export include files.

      2. declare a second module in an Android.mk file for the second module (class fooMate). The second module will use LOCAL_SHARED_LIBRARIES to pull in the first one.

      See the Android NDK documentation for details. When you download the Android NDK from http://developer.android.com/tools/sdk/ndk/index.html, you’ll find a web page documentation.html that has information about LOCAL_EXPORT_C_INCLUDES and LOCAL_SHARED_LIBRARIES

And now it's your turn ... comment here or on Twitter (@Androider)

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s