It seems to me that gradle Android plugin does not include versioned .so libraries into .aar archive.
My case:
I need to load some library in Android application that requires another library: libz.so.1
I do have libz.so and libz.so.1 in my native libraries directory.
After running gradle there is only libz.so library in created .aar archive.
It results in java.lang.UnsatisfiedLinkError: dlopen failed: library "libz.so.1" not found exception when I try to run my application.
It looks like my otherlibrary tries to load only that specific version of libz.
readelf -d otherlibrary.so also tells me that it wants specific library:
0x00000001 (NEEDED) Shared library: [libz.so.1]
Both libraries that I want to load are third party libraries.
It looks like I need to find a way for one of the following:
pack libz.so.1 into .aar archive, so otherlibrary.so could easily link with it
load libz.so and somehow make otherlibrary.so to link with it
but I'm running out of ideas, how to do it.
Part of build.gradle that builds .aar archive:
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
libs directory contains both libz.so and libs.so.1 libraries. Only libz.so is assembled into .aar file.
This page: https://developer.android.com/studio/projects/android-library#aar-contents
suggests me that only /jni/abi_name/name.so pattern is supported.
Looking forward for some help.
Related
For Intellij (and Android Studio) I built a JNI shared library that links to boost libraries that I'd link to include in my Android app. I called System.loadLibrary on my .so file but it fails to find boost libraries when I run it. I get the following error:
java.lang.UnsatisfiedLinkError: dlopen failed: library "libboost_unit_test_framework-clang-mt-x32-1_77.so.1.77.0" not found: needed by /data/app/~~28p8gv9ihFbZAejYd9c9yw==/sensoft.applications.dt1demo-0IEJ8o6cHOk0kputNbnbNQ==/base.apk!/lib/x86/libssi_utils_java.so in namespace classloader-namespace
Even though the boost .so files are there in the libs/x86 directory (I built them in x86 with the android toolchain to run on my emulator) but it did manage to find the .so file that I built in the same directory.
I placed my .so file and all the required boost .so files in the libs/x86 directory and included the following as a jni source in my build.gradle file:
sourceSets {
main.jniLibs.srcDirs = ['libs']
}
But my application can't seem to find the boost shared libraries specifically.
Android's install process will not extract libraries that don't have the suffix .so. You have to remove the version suffix of the library (which serves no purpose on Android anyway, because libraries are not all installed to a single common path).
I've an android app written in Android Studio 3.0.1 and with compileSdkVersion 26. This app depends on a module written in native code (c++). The native code in the module depends on a third party shared library (.so) file. In the Cmake file this third party library is included using the standard method, e.g:
add_library( my-module-lib
SHARED
src/main/cpp/file1.cpp
src/main/cpp/file2.cpp )
add_library( libthird_party_library SHARED IMPORTED )
set_target_properties( libthird_party_library PROPERTIES IMPORTED_LOCATION ${pathToProject}/src/main/jniLibs/${ANDROID_ABI}/libthird_party_library.so)
target_link_libraries( my-module-lib libthird_party_library )
My module is then added as dependency to the main app and the native code is loaded using:
System.loadLibrary("my-module-lib");
This all works fine, however I need to update the third party library. I thought this was just as simple as replacing the old .so files for the new ones. But this leads to an UnsatisfiedLinkError:
java.lang.UnsatisfiedLinkError: dlopen failed: library "libthird_party_library.so.2" not found
at java.lang.Runtime.loadLibrary0(Runtime.java:989)
at java.lang.System.loadLibrary(System.java:1567)
It should be noted that android now searches for a ".so.2" file instead of a ".so" file. So I would think that for some reason the old one is still included somewhere, and because of name collision the new one is renamed to "*.2". So I tried these things to get rid of it:
1) [not working] Delete everything, recompile everything
Just deleting everything, that is:
the .externalNativeBuild folder for my module
the build folder for my module
the build folder for my app
And rebuild everything, that is:
Build > Clean Project
Build > Make Module "my module"
Build > Refresh Linked C++ Projects
Build > Rebuild Project
But this still leads to the same UnsatisfiedLinkError
2) [not working] Changing the name of the .so file
Changing the name of the updated library to "libthird_party_library_NEW.so" and changing the cmake file:
set_target_properties( libthird_party_library PROPERTIES IMPORTED_LOCATION ${pathToProject}/src/main/jniLibs/${ANDROID_ABI}/libthird_party_library_NEW.so)
Same error
3) [working, not a good solution] Rename the old ".so" file and include new one
One "solution" which does work is renaming the old ".so" files using refactor > rename to something like "libthird_party_library_OLD.so" and then copying the new third party libraries using the standard name "libthird_party_library.so". But this is of course not a very nice solution, because the old non needed library will be included in the apk.
It turned out to be a problem with linking a versioned shared library in android studio. Similar as in this question and as described here.
The problem is that the internal version number of "libthird_party_library.so" is actually "libthird_party_library.so.2". This can be found by running objdump under linux on the .so file, that is:
objdump -p libthird_party_library.so | grep so
which outputs:
libthird_party_library.so: file format elf64-little
NEEDED libm.so
NEEDED libc.so
NEEDED libdl.so
SONAME libthird_party_library.so.2
required from libdl.so:
required from libm.so:
required from libc.so:
Since the file name as seen by android is "libthird_party_library.so" and android tries to load "libthird_party_library.so.2" it can obviously not find the required library.
Solution
An obvious solution would be to change the filename to "libthird_party_library.so.2", but this doesn't work because android studio only includes libraries ending on .so in the apk (aaargh).
The solution is to change the internal version number to "libthird_party_library_2.so", renaming the file to "libthird_party_library_2.so" and changing the Cmake file to reflect this change.
1) Changing the internal version number can be done under linux with:
rpl -R -e libthird_party_library.so.2 libthird_party_library_2.so libthird_party_library.so
Where the first argument is the internal version number, the second the one we need to change it to and the third the filename. MAKE SURE THE LENGTH OF THE OLD INTERNAL VERSION NUMBER IS THE SAME AS THE NEW ONE!!
2) Change the filename to "libthird_party_library_2.so" using your favorite tool
3) Change this line in the CMake file to
set_target_properties( libthird_party_library PROPERTIES IMPORTED_LOCATION ${pathToProject}/src/main/jniLibs/${ANDROID_ABI}/libthird_party_library_2.so)
Hope this helps!
I'm struggling to include a prebuilt shared library in my android project
The library in question is libusb, which the NDK part of my android project requires.
Everything is compiling and linking OK, i.e. the project is building successfully, but on installing the apk on my device the app is crashing.
The relevant error msg from monitor is:
java.lang.UnsatisfiedLinkError: dlopen failed: library "libusb1.0.so" not found
what i've tried so far is adding the following to my app/build.gradle:
sourceSets{
main {
// let gradle pack the shared library into apk
jniLibs.srcDirs = '/home/me/third-party/libusb-1.0.21/android/libs/'
}
in CMakeLists.txt i've added:
set(libusb_DIR $ENV{HOME}/third-party/libusb-1.0.21/android/libs)
set(libusb_LIB usb1.0)
link_directories( ${libusb_DIR}/${ANDROID_ABI}/ )
target_link_libraries( ${libusb_LIB} )
I've even tried creating a app/src/main/jniLibs dir and manually copying the armeabi-v7a version of the shared lib, libusb1.0.so, in there.
Still getting same error message in Monitor after the apk has been installed..
Give a try to this one, instead of taking the path from env you should try ${CMAKE_SOURCE_DIR}
set(usb_DIR ${CMAKE_SOURCE_DIR}/../../../../libs)
add_library(libusb SHARED IMPORTED)
set_target_properties(libusb PROPERTIES IMPORTED_LOCATION
${usb_DIR}/libs/${ANDROID_ABI}/libusb1.0.so)
target_link_libraries(${libusb})
Android Studio 2.2 introduces cmake external build system. The problem is that documentation is really lacking and I do not know how should I link third party libraries? I've tried cmake directive target_link_libraries:
target_link_libraries(native-lib libs/libSomething.so)
And it "works" in that app compiles but then I get dlopen error at runtime because libSomething.so has not been packaged with application. The libs directory is under "app" if that changes anything and I've started with default JNI project generated by Android Studio 2.2...
[Update]
I've tried putting libSomething.so under app/src/main/jniLibs/armeabi-v7a but now the "main" (native-lib) library is not packaged.
[Update2]
I've added source set config that includes cmake output dir and this works but is ugly as hell and is not really a permanent solution...
sourceSet
{
main
{
jniLibs.srcDirs = [ "libs", ".externalNativeBuild/cmake/debug/obj"]
}
}
For now I ended up copying libSomething.so to cmake library output directory in a post build step. This works because it turns out that Android Studio copies into apk EVERYTHING that is in that directory.
Command in cmake is the following:
add_custom_command(TARGET native-lib POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI}/libSomething.so
${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libSomething.so
)
For now, you could also put your shared libs into directory, and configure jniLibs to point to that directory, that will pack it. One sample is here: https://github.com/googlesamples/android-ndk/tree/master/hello-libs, follow gperf see if that helps. This way app not depending on the generated build folders. When android studio added packing libs, the jniLibs workaround is not necessary anymore
I've made an android library project that uses some native libraries.
I've made the jni wrapper and put the native libs (.so) in the libs// folders. The native libs are compiled using cmake, for armeabi, armeabi-v7a, x86 and mips.
I export this project to a jar and put this jar into a "normal" android project. I then export this project to an apk and can see that my libs are bundles into it.
However, when i install the apk, the libs corresponding to the device are not copied into /data/data/com.my.app/lib and obviously, running the app complains about not finding the libs (UnsatisfiedLinkError).
I've search through SO and everywhere i can but found no answer that solved my case.
i'm using Eclipse, btw
Thanks for your help
UPDATE
OK, i've read the doc in the ndk and seen the examples, and unfortunately, i can't see the solution.
The ndk build the c code into shared libs and places them into the appropriated location in the project. But it doesn't generate anything that says that the libs must be installed with the apk.
My goal is to provide an android library (so a jar), that can be included within an android application. I don't see the real difference between what i'm doing (compile the c libs using cmake and package the jni and java compiled classes into a jar) and what is done with android.mk
If you see what i'm missing, feel free to tell me (even if its obvious).
thanks
UPDATE
i've made a dirty hack: in the libs folder of my application, i've put the jar file containing my classes and my native libs and a copy of the .so files for each arch. Suprise, the libs are no installed in /data/data/com.me.myapp/lib
It seems to confirm that it's a packaging problem.
I export this project to a jar and put this jar into a "normal"
android project. I then export this project to an apk and can see that
my libs are bundles into it.
The issue is that the Android packaging system doesn't handle with binary assets in JARs. For your application project to find and include the generated .so files, you need it to reference the library project as an 'Android library project':
Did you call ndk-build command?
See description below for details.
http://developer.android.com/tools/sdk/ndk/index.html
You can build the shared libraries for the sample apps by going into /samples// then calling the ndk-build command. The generated shared libraries will be located under /samples//libs/armeabi/ for (ARMv5TE machine code) and/or /samples//libs/armeabi-v7a/ for (ARMv7 machine code).