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?
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!
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)
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.
Related
I have recently started working with NDK and am stuck with a problem. I basically want to use an external static library (.so) into my application. Here's what I've tried till now.
Create a library
1) Add a method in the Java class with native keyword
public native static void FibNR ();
2) In the terminal navigate to the project folder and run the following command
mkdir jni
javah -jni -classpath bin/classes/ -d jni/ com.example.fibonaccinative.FibLib
3) Refresh project and add a C file corresponding to the generated H file.
4) Create a Android.mk file in the JNI folder and add the following code in it
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
OPENCV_LIB_TYPE :=STATIC
LOCAL_SRC_FILES := com_example_fibonaccinative_FibLib.c
LOCAL_MODULE := com_example_fibonaccinative_FibLib
include $(BUILD_SHARED_LIBRARY)
5) Build the code using the following command
/Developer/android-ndk-r9b/ndk-build all
The above steps execute successfully and I'm able to view the results of the c code also. Now I want to create a new application and use the generated .so file (com_example_fibonaccinative_FibLib.so) into it. For this I do the following
Use the library
follow steps 1, 2 and 3 from above for the new application
4) Create a Android.mk file in the JNI folder and add the following code in it
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
OPENCV_LIB_TYPE :=STATIC
LOCAL_SRC_FILES := com_example_usingstaticlibrary_LibraryTest.c
LOCAL_MODULE := com_example_usingstaticlibrary_LibraryTest
LOCAL_SHARED_LIBRARIES :=libcom_example_fibonaccinative_FibLib.so
LOCAL_LDLIBS := -L$(SYSROOT)/usr -llog
include $(BUILD_SHARED_LIBRARY)
5) Build the code using the following command
/Developer/android-ndk-r9b/ndk-build all
I'm not sure what to do next. What I think is I need to call the function of the library into com_example_usingstaticlibrary_LibraryTest.c. But doing so gives me an error saying
'com_example_fibonaccinative_FibLib' undeclared
EDIT 1:
1) In project 2 (where I want to use the Prebuilt Shared Library (.so)) I copied the library into the 'jni' folder
2) Changed the Android.mk file with the below text.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libcom_example_fibonaccinative_FibLib
LOCAL_SRC_FILES := libcom_example_fibonaccinative_FibLib.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := com_example_usingstaticlibrary_LibraryTest
LOCAL_SHARED_LIBRARIES := libcom_example_fibonaccinative_FibLib
LOCAL_SRC_FILES := com_example_usingstaticlibrary_LibraryTest.c
include $(BUILD_SHARED_LIBRARY)
3) Added the .h file from the old project (com_example_fibonaccinative_FibLib.h) into the 'jni' folder
4) Changed the source of com_example_usingstaticlibrary_LibraryTest.c to
#include "com_example_usingstaticlibrary_LibraryTest.h"
#include "com_example_fibonaccinative_FibLib.h"
JNIEXPORT jlong JNICALL Java_com_example_usingstaticlibrary_LibraryTest_callLibraryFunction
(JNIEnv *env, jclass class) {
Java_com_example_fibonaccinative_FibLib_fibNR(env, class, 500l);
return -500l;
}
Clean/Build results into the following error
undefined reference to 'Java_com_example_fibonaccinative_FibLib_fibNR'
Edit 2:
With reference to this I've edited the Android.mk as follows
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := com_example_fibonaccinative_FibLib
LOCAL_SRC_FILES := libcom_example_fibonaccinative_FibLib.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := com_example_usingstaticlibrary_LibraryTest
LOCAL_SRC_FILES := com_example_usingstaticlibrary_LibraryTest.c
LOCAL_SHARED_LIBRARIES := com_example_fibonaccinative_FibLib
include $(BUILD_SHARED_LIBRARY)
The NDK build command compiles the code without error. When trying to load the library it gives the following error
Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Lcom/example/usingstaticlibrary/LibraryTest
When I remove LOCAL_SHARED_LIBRARIES := com_example_fibonaccinative_FibLib it works fine but I cannot use any function of the Prebuilt shared library.
Edit 3:
I tried a few more things including the suggestion by #jcm but nothing worked. I'm now attaching the source code. It has both the projects (Cleaned version to reduce size).
FibonacciNative: Contains the first project.
UsingStaticLibrary: Contains the second project. I intend to use the Prebuilt Shared Library of the first project into the second one.
You say you want to include a precompiled static library .a, but the file you reference is a shared library .so. If you still want to include a static library, here comes the solution and explanation:
LOCAL_SHARED_LIBRARIES :=libcom_example_fibonaccinative_FibLib.so
You're trying to include the static library as a shared one, looks like an accidental slip from using the previous shared library entry as reference :).
Upon that, Android makefiles still requires you to actually make a module for the external library.
include $(CLEAR_VARS)
LOCAL_MODULE := com_example_fibonaccinative_FibLib
LOCAL_SRC_FILES := com_example_fibonaccinative_FibLib.a
include $(PREBUILT_STATIC_LIBRARY)
Then you can simply add it as a dependency:
LOCAL_STATIC_LIBRARIES := com_example_fibonaccinative_FibLib
But if you're actually trying to include an external shared library. You would need to change the above to the following:
include $(CLEAR_VARS)
LOCAL_MODULE := com_example_fibonaccinative_FibLib
LOCAL_SRC_FILES := com_example_fibonaccinative_FibLib.so
include $(PREBUILT_SHARED_LIBRARY)
and include it as a shared library.
LOCAL_SHARED_LIBRARIES := com_example_fibonaccinative_FibLib
Hope I helped!
Disclaimer: I have not actually tried to compile an example, simply referencing my knowledge and quick research.
References:
Android NDK: Static library used is different than precompiled library specified
As you're including libcom_example_fibonaccinative_FibLib.so as LibraryTest's shared library, in your java static method, you should load it before loading LibraryTest:
System.loadlibrary(com_example_fibonaccinative_FibLib);
System.loadLibrary(LibraryTest);
Update:
After checking your code, LibraryTest.java should look like:
package com.kochartech.usingstaticlibrary;
public class LibraryTest {
public native static long callLibraryFunction();
static {
System.loadLibrary("com_kochartech_fibonaccinative_FibLib");
System.loadLibrary("com_kochartech_usingstaticlibrary_LibraryTest");
}
}
I created a static C++ library for testing. It only defines a class, MyLibrary, and the constructor MyLibrary::MyLibrary(). I built it in qtcreator, and got a libMyLibrary.a file, which is a prebuilt static library.
I would like to use this library in an Android project (using the NDK). In a working NDK test project, I therefore added a folder called inc at the same level as jni, in which I put libMyLibrary.a and its corresponding header mylibrary.h.
My Android.mk is as follows:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := MyLibrary
LOCAL_SRC_FILES := ../inc/libMyLibrary.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := helloJNI
LOCAL_SRC_FILES := mainActivity.cpp
LOCAL_C_INCLUDES += inc
LOCAL_STATIC_LIBRARIES := MyLibrary
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
The ndk-build command compiles without any error. But my static library is apparently not linked (i.e. it does not appear in obj/local/armeabi/objs).
I have tried to include it in my mainActivity.cpp file, but even though the header (mylibrary.h) is found, the library is not and I cannot create an object as in:
MyLibrary test = MyLibrary();
What am I doing wrong? I have read tens of similar questions on StackOverflow, but I still don't get it.
try to use this:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := helloJNI
LOCAL_SRC_FILES := mainActivity.cpp
LOCAL_C_INCLUDES := $(LOCAL_PATH)/inc/
LOCAL_LDLIBS := -llog -L$(LOCAL_PATH)/inc/ -lMyLibrary
include $(BUILD_SHARED_LIBRARY)
move libMyLibrary.a & mylibrary.h to jni/inc/libMyLibrary.a
I have read other questions similar to this on stack overflow but they are not having same like scenario.
I have FreeImage.a(23 MB file ) file which precompild static library for android.
I also have source code of FreeImage Project which have header files.
I want to build .SO file from (.a) file I have with my JNI code(FreeImageCompilation.cpp)
Below code compiles fine but it does produces SO File of (5KB only ) whre (*.a file is 23 MB )?
can somebody check if my code below for using *.a file is correct or not ?
In My Android.mk I have following code.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := FreeImage
LOCAL_SRC_FILES := libFreeImage.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/FreeImage/Source/
include $(PREBUILT_STATIC_LIBRARY)
#My Own SO file
LOCAL_STATIC_LIBRARIES := FreeImage
include $(CLEAR_VARS)
LOCAL_MODULE := FreeImageSo
LOCAL_SRC_FILES := FreeImageCompilation.cpp
LOCAL_STATIC_LIBRARIES := FreeImage
include $(BUILD_SHARED_LIBRARY)
To answer the question from the title, here is how you can create shared library from a static library:
# static library 1
include $(CLEAR_VARS)
LOCAL_MODULE := lib1
LOCAL_SRC_FILES := lib1.cpp
include $(BUILD_STATIC_LIBRARY)
# static library 2
include $(CLEAR_VARS)
LOCAL_MODULE := lib2
LOCAL_SRC_FILES := lib2.cpp
include $(BUILD_STATIC_LIBRARY)
# this shared library will have all symbols from two above libraries
include $(CLEAR_VARS)
LOCAL_MODULE := lib_shared
LOCAL_SRC_FILES := empty.cpp
LOCAL_WHOLE_STATIC_LIBRARIES := lib1 lib2
include $(BUILD_SHARED_LIBRARY)
Important option to note is LOCAL_WHOLE_STATIC_LIBRARIES. If you use regular LOCAL_STATIC_LIBRARIES, because you don’t use any symbols from lib1 or lib2 in lib_shared, they will be stripped at link time. To prevent this from happening LOCAL_WHOLE_STATIC_LIBRARIES adds following options to link line to make sure symbols are not stripped:
-Wl,--whole-archive -llib1 -llib2 -Wl,--no-whole-archive
More info in my blog post here: http://gosuwachu.io/
It's totally correct but you seem to be confused about concepts. Your static library is not included in your shared library as you seem to expect. Your static library is just linked to the shared library. In the end, your program needs an .so file and an .a to function properly, not just one big .so that holds everything.
I have created a .so file that exposes a native C call to Java via JNI. This works well and I can deploy the app onto my Android system if I just use system libraries in my C code. However, if I want to make calls to functions in other .so files, I cannot get my project to link correctly.
For example, say I have the "libotherso.so" file which contains APIs defined in C that I can call from the "MyJNILibrary.c" code I'm using to generate "libMyJNILibrary.so".
I tried to change my Android.mk file as follows:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := MyJNILibrary
LOCAL_SRC_FILES := MyJNILibrary.c
LOCAL_LDLIBS += -lotherso
include $(BUILD_SHARED_LIBRARY)
But when I call ndk-build on this, I get errors finding -lotherso. Where do I put the "libotherso.so" file so that ndk-build can pick it up?
Thanks for any help you can provide.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := otherso
LOCAL_SRC_FILES := ../lib/libotherso.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := MyJNILibrary
LOCAL_SRC_FILES := MyJNILibrary.c
LOCAL_SHARED_LIBRARIES := otherso
include $(BUILD_SHARED_LIBRARY)
Note that LOCAL_SRC_FILES is relative to your LOCAL_PATH.
Don't forget to load your dependency before your own JNI library:
static {
System.loadLibrary("otherso");
System.loadLibrary("MyJNILibrary");
}
I have built OpenSSL for Android(using ndk-build) and have linked it to my program. Compilation and linking works, but I get a run time error of UnsatisfiedLinkError.
More specifically, the program breaks on this code:
public class TestActivity extends Activity
{ // load the library - name matches jni/Android.mk
static {
System.loadLibrary("crypto2");
System.loadLibrary("ssl2"); // <=BREAKS HERE!
System.loadLibrary("Test");
}
It specifically breaks when loading ssl2, so it seems it was able to load crypto2.
I think I need to tell ssl2 that it use cryto2, I tried to do that on the Android.mk file, but it didn't help.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libcrypto2
LOCAL_SRC_FILES := OpenSSL/libcrypto2.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libssl2
LOCAL_SRC_FILES := OpenSSL/libssl2.so
LOCAL_SHARED_LIBRARIES := libcrypto2
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS) # Here we give our module name and source file(s)
LOCAL_C_INCLUDES := D:/DevelopTools/OpenSSL-WIN32/include $(LOCAL_PATH)/../../../Framework/Applications/FW2Prototype $(LOCAL_PATH)/../../../Graphics/Libraries/Common $(LOCAL_PATH)/../../../Input/Libraries/Common $(LOCAL_PATH)/../../../Audio/Libraries/Common $(LOCAL_PATH)/../../../Audio/Libraries/NullAudio $(LOCAL_PATH)/../../../Network/Libraries/Common
LOCAL_MODULE := Test
LOCAL_SRC_FILES := Test.cpp Reference.cpp JNINetwork.cpp JNIGraphics2D.cpp JNIInput.cpp JNIAudio.cpp Applications/FatalWars2/FatalWars2.cpp /../../../Audio/Libraries/NullAudio/NullAudio.cpp
LOCAL_SHARED_LIBRARIES := libcrypto2 libssl2
include $(BUILD_SHARED_LIBRARY)
Any idea what is wrong?
Thanks,
I "solved" this.
I simpley built openssl into a single so file. I built both crypto and ssl statically, so ssl's dependency in crypto was hidden inside the static linking.
The issue is apparently that I couldn't load the prebuilt shared library ssl, because it was dependant of another prebuilt shared library(crypto). Maybe it's possible to load such libraries somehow, but my solution was just to static link crypto into ssl and then into a shared library called openssl.