1

I build a C++ library manually using the Android SDK compiler. The result is libMyUtils.a.

I'm using the following in my Java/JNI test application:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := MyUtils
LOCAL_SRC_FILES := ../../../../libs/libMyUtils.a

include $(PREBUILT_STATIC_LIBRARY)

LOCAL_MODULE    := AndroidTests
LOCAL_STATIC_LIBRARIES    := MyUtils

include $(BUILD_SHARED_LIBRARY)

When I build the console shows the following:

[armeabi] Install        : libMyUtils.so => libs/armeabi/libMyUtils.so

Now for some bizarre reason the library ../../../../libs/libMyUtils.a is a couple megabytes, but the library libs/armeabi/libMyUtils.so is only 5KB. Isn't it supposed to be the same library?

When I run my test application I get UnsatisfiedLinkError. Obviously the native function I'm calling isn't located in the library. What am I doing wrong?

Emmanuel
  • 16,791
  • 6
  • 48
  • 74

3 Answers3

0

A full explanation, as your Android.mk does not make much sense to me: sorry if you already know some of that.

Static libraries should be pure C/C++, and wrapped using the Android NDK in order to be usable from Java.

For example, assuming your static library is built from a simple .c and .h files:

highfive.h:

int giveMeFive();

highfive.c:

#include "highfive.h"

int giveMeFive() {
  return 5;
}

This can be compiled as a static library using the Android NDK compiler, which apparently you already know how to do: this will give us a highfive.a library.

In this form, this library is unusable from Java, but it can be wrapped using the Android NDK. See the Android NDK documentation for naming conventions etc...

highfiveWrapper.c:

#include "highfive.h"

jint
Java_your_package_name_HighFive_giveMeFive(JNIEnv *env, jobject o) {
  return (jint) giveMeFive();
}

and its corresponding Java file:

package your.package.name;

class HighFive {
  static {
    System.loadLibrary("highfive");
  }

  public native int giveMeFive();
}

Now, how do we compile all this to get it to work:

Android.mk:

include $(CLEAR_VARS)
LOCAL_MODULE            := libhighfive-prebuilt
LOCAL_SRC_FILES         := path/to/highfive.a
LOCAL_EXPORT_C_INCLUDES := path/to/highfive.h/folder
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE      := highfive
LOCAL_SRC_FILES   := path/to/highfiveWrapper.c
LOCAL_STATIC_LIBRARIES := libhighfive-prebuilt
include $(BUILD_SHARED_LIBRARY)

And there you should be able to use your native library as you wished to!

Hope this helps!

mbrenon
  • 4,851
  • 23
  • 25
  • OK well the pre-built library contains the wrapper. I think what is happening is that in the last local module the symbols are all stripped since I don't have nay source files to use them. – Emmanuel Nov 22 '13 at 14:14
  • Yes, that's probably what happens: no LOCAL_SRC_FILES implies that there is no reference to the static library, which is considered "unused" and thus stripped. – mbrenon Nov 22 '13 at 14:15
  • I can't think of any other solution than putting your NDK wrapper in the shared library. I don't think it's possible to have it inside a static library. – mbrenon Nov 22 '13 at 14:21
  • That leaves the following things to investigate: 1- how to build a dynamic library for MyUtils from the command line, or 2- how to prevent stripping in Android.mk. – Emmanuel Nov 22 '13 at 15:49
0

Usually, static libraries contain many objects, which contain many functions, of which many are unused. That's why the linker only pulls the referenced objects from static libraries. So, in the example given by @mbrenon, highfiveWrapper.c defines which components of highfive.a will be linked into highfive.so.

But in your setup, you need whole static library to be loaded. OK, there is a special word for it: LOCAL_WHOLE_STATIC_LIBRARIES.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := MyUtils
LOCAL_SRC_FILES := ../../../../libs/libMyUtils.a

include $(PREBUILT_STATIC_LIBRARY)

LOCAL_MODULE    := AndroidTests
LOCAL_WHOLE_STATIC_LIBRARIES    := MyUtils

include $(BUILD_SHARED_LIBRARY)
Emmanuel
  • 16,791
  • 6
  • 48
  • 74
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • 1
    I've already tried this solution. I don't know why but it didn't seem to work. It makes sense though. After this my library was still empty. But I've found the solution. – Emmanuel Nov 25 '13 at 14:08
0

In the end the solution was to build the MyUtils library as a prebuilt shared library. This way the linker doesn't strip anything. Then I modified my makefile as follows:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := MyUtils
LOCAL_SRC_FILES := ../../../../libs/libMyUtils.so

include $(PREBUILT_SHARED_LIBRARY)

LOCAL_MODULE    := AndroidTests
LOCAL_SHARED_LIBRARIES    := MyUtils

include $(BUILD_SHARED_LIBRARY)

Notice the .so extention and the PREBUILT_SHARED_LIBRARY and BUILD_SHARED_LIBRARY scripts.

Emmanuel
  • 16,791
  • 6
  • 48
  • 74