10

I am trying to build my Android (native) project using cmake (migrating it from gradle experimental plugin where it used to build and run fine).

I have some native code(will call it 'a') which uses another external prebuilt library code (will call it 'b') and I linked the two like this: (according to https://developer.android.com/studio/projects/configure-cmake)

cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -frtti -fno-common -fexceptions")

include_directories(
#a's include files' paths
#b's include files' paths)

file(GLOB_RECURSE A_SOURCES
#a's source files' paths)

add_library(a_lib SHARED ${A_SOURCES})
add_library(b_lib SHARED IMPORTED)
set_target_properties(b_lib PROPERTIES IMPORTED_LOCATION "b's .so path")
target_link_libraries(a_lib b_lib)

I got past the compilation and linking steps and the android studio goes ahead to install the APK on my device. However, after launch, the app freezes with the following in the logcat:

E/ExceptionHandler: Uncaught Exception java.lang.UnsatisfiedLinkError: dlopen failed: library "libb_lib.so" not found
at java.lang.Runtime.loadLibrary0(Runtime.java:989)
at java.lang.System.loadLibrary(System.java:1530)
at com.mm.projectname.model.libloadingclassname.<clinit>(libloadingclassname.java:99)

I know this is happening because b_lib's .so is not there in the apk. And I can see b's symbols along with a's symbols in a's shared library.

So my question is how can I

  1. either package my prebuilt b's .so in the apk
  2. or prevent the system from looking for b.so in the libs folder and make it look for b's symbols in a's .so only.

I searched a lot for similar posts and questions (eg one and two ) but i cant get anything to work. I am really looking for the right way to do this - which wont create problems in the future (like changing the targetSDKversion). I also tried building the prebuilt lib with the latest ndk version.

It's possible that I am doing a very small mistake, and would really appreciate if someone could point it out.

Thanks in advance

smitt
  • 304
  • 1
  • 3
  • 8
  • It should be enough if you put libb_lib.so into jniLibs. What architectures was the prebuilt library built for, what architectures are you building for and what's the architecture of the device you are trying this on? – Someone Aug 29 '18 at 12:20
  • There is a loading library issue. try`fileTree(dir: 'libs', include: '**/*.so')`. `compile fileTree(dir: 'libs', include: ['.jar','.so'])` – Akhila Aug 29 '18 at 12:21
  • @Alex do you mean like [this](https://developer.android.com/studio/projects/gradle-external-native-builds#jniLibs)? i tried that - didnt seem to solve the problem. prebuilt lib is built for arm64-v8a, armeabi-v7a, x86, and x86_64. and i tried this on armeabi-v7a architecture. – smitt Aug 29 '18 at 13:34
  • I think it should be fine if you place libb like this: src/main/jniLibs/armeabi-v7a/libb_lib.so. There shouldn't be anything you have to add in gradle for that since it is the default location to copy native libraries from. – Someone Aug 29 '18 at 13:37
  • @Alex putting the shared library in jniLibs didnt work either - i am still getting the same error. – smitt Aug 30 '18 at 09:29
  • @Akhila i put tried by putting the second command in dependencies in build.gradle - that didnt help - dont know where the first command will come? – smitt Aug 30 '18 at 09:30
  • Could you add your app build.gradle to your question? – Someone Aug 30 '18 at 09:32

2 Answers2

5
  1. Create a jniLibs folder in your app's main directory (e.g.: /app/src/main).
  2. Make another folder named according to the architecture.
  3. Store your .so file in this folder.
PEEHU RAJ
  • 51
  • 1
  • 3
  • What if my native libraries are inside aar file from java library? – Kamil Sep 19 '22 at 17:05
  • Thank! This work for me, in the Kotlin Android project. – 孙欣乐 Dec 08 '22 at 13:08
  • @Kamil i have same problem. Do you have any solution? – Pravin Suthar Feb 22 '23 at 10:36
  • @PravinSuthar. In my case problematic classes are drivers for RFID scanner. They are not working on x86 platform. I have "unsatisfied link error" when I start my app on x86 emulator (for better performance). I had to create a facade between that library and my app. Inside/behind my facade I catch these errors and provide a mockup class instead of real. These drivers are written for arm architecture. – Kamil Feb 22 '23 at 23:36
1

A little late, but someone else might be interested (or even the op).

I had the same situation as the op and I tried many different things. One of those was to try and compile the project with an earlier version of NDK (I think r14b, but I am not enitrely sure). I got a different error and managed to track down the issue which is well described here, in sections "Invalid DT_NEEDED Entries" and "Missing SONAME". Additionally, the problem is described in this particular question and has received an adequate answer.

If you cannot recompile the shared library you are using in order to include the SONAME, like in my case, you can do the following thing I did and managed to work:

  • Include the library in jniLibs folder in your project tree, so it is packed in your APK but do not link against it.
  • Track down the symbols of the functions you want to incorporate using any method described here.
  • Create function pointers of the corresponding functions in your C++ code.
  • Load the shared library at runtime and map to those functions.

Example Code

Header:

private:

typedef uint32_t (*InitX_t)();
typedef uint32_t (*DoX_t)();
typedef uint32_t (*GetX)(uint32_t, char*);

InitX_t InitX;
DoX_t DoX;
GetX_t GetX;

CPP

void *handle = dlopen("libMyLib.so", RTLD_NOW);

if(handle == nullptr)
{
    __android_log_print(ANDROID_LOG_INFO, "My Class", "Could not load library");
}
else
{
    __android_log_print(ANDROID_LOG_INFO, "My Class", "Library loaded");
}

InitX = (InitX_t)dlsym(handle, "InitX_SYMBOL");
ScanX = (ScanX_t)dlsym(handle, "ScanX_SYMBOL");
GetX = (GetX_t)dlsym(handle, "GetX_SYMBOL");

if(ScanX == nullptr || GetX == nullptr || InitX == nullptr)
{
    __android_log_print(ANDROID_LOG_INFO, "My Class", "Could not load functions.");
}

If done correctly, you should be able, now, to use the functions as usual. I understand, though, that it's not the most trivial process to go if you are a beginner.

Manos
  • 143
  • 9