user7028020
user7028020

Reputation:

Using a prebuilt shared Library in Android Studio (cmake)

I want to use a C++ shared library inside my Android App. I tried to follow along the hello-libs example from Google's NDK samples, but somehow it doesn't work out.

It seems, that my library isn't packed into the APK. All tutorials i found are using *.mk-files, but I want to use cmake.

This is my CMakeLists.txt:

# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)

# native lib
add_library(native-lib SHARED
            native-lib.cpp)

set(IMPORT_DIR ${CMAKE_SOURCE_DIR}/../jniLibs)

# shared lib
add_library(shared-lib SHARED IMPORTED)
set_target_properties(shared-lib PROPERTIES IMPORTED_LOCATION
                      ${IMPORT_DIR}/dynamic/lib/${ANDROID_ABI}/libLibrary.so)
target_include_directories(native-lib PRIVATE
                           ${IMPORT_DIR}/dynamic/include)

# linking libs
target_link_libraries(native-lib
                      android
                      shared-lib
                      log)

As soon as I link the shared-lib along with the others, I get the following error, when running the app:

E/art: dlopen("/data/app/my.package.name-1/lib/arm/libnative-lib.so", RTLD_LAZY) failed: dlopen failed: library "C:/projects/HelloLibs/app/src/main/cpp/../jniLibs/dynamic/lib/armeabi-v7a/libLibrary.so" not foun
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
                  Process: my.package.name, PID: 29266
                  java.lang.UnsatisfiedLinkError: dlopen failed: library "C:/projects/HelloLibs/app/src/main/cpp/../jniLibs/dynamic/lib/armeabi-v7a/libLibrary.so" not found
                      at java.lang.Runtime.loadLibrary(Runtime.java:371)
                      at java.lang.System.loadLibrary(System.java:988)
                      at my.package.name.MainActivity.<clinit>(MainActivity.java:11)
                      at java.lang.reflect.Constructor.newInstance(Native Method)
                      at java.lang.Class.newInstance(Class.java:1690)
                      at android.app.Instrumentation.newActivity(Instrumentation.java:1078)
                      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2543)
                      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2771)
                      at android.app.ActivityThread.access$900(ActivityThread.java:177)
                      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1432)
                      at android.os.Handler.dispatchMessage(Handler.java:102)
                      at android.os.Looper.loop(Looper.java:135)
                      at android.app.ActivityThread.main(ActivityThread.java:5912)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at java.lang.reflect.Method.invoke(Method.java:372)
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1405)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1200)

It looks like the library is not found on the device. Otherwise it looks like the librarie's path is still the one on my local maschine.

Upvotes: 11

Views: 13104

Answers (4)

Mykola Khyliuk
Mykola Khyliuk

Reputation: 1423

Another solution for packing the prebuilt libraries into your APK - linking them to jniLibs.

For example, I don't use direct placement of libraries (because they already exist elsewhere - in my case in docker image with all required prebuilts, SDK, NDK, etc.) but instead of it I just use linking to them to put them in APK. And if such libraries are also needed at build time, cmake can simply uses find_package().

    /app/src/main$ tree
    .
    ├── jniLibs
    │   ├── arm64-v8a
    │   │   ├── libCommonAPI.so -> /opt/thirdparty/android-29/arm64-v8a/capicxx-core-runtime/lib/libCommonAPI.so
    │   │   ├── libCommonAPI-SomeIP.so -> /opt/thirdparty/android-29/arm64-v8a/capicxx-someip-runtime/lib/libCommonAPI-SomeIP.so
    │   │   ├── libvsomeip3-cfg.so -> /opt/thirdparty/android-29/arm64-v8a/vsomeip/lib/libvsomeip3-cfg.so
    │   │   ├── libvsomeip3-e2e.so -> /opt/thirdparty/android-29/arm64-v8a/vsomeip/lib/libvsomeip3-e2e.so
    │   │   ├── libvsomeip3-sd.so -> /opt/thirdparty/android-29/arm64-v8a/vsomeip/lib/libvsomeip3-sd.so
    │   │   └── libvsomeip3.so -> /opt/thirdparty/android-29/arm64-v8a/vsomeip/lib/libvsomeip3.so
    │   ├── armeabi-v7a
    │   │   ├── libCommonAPI.so -> /opt/thirdparty/android-29/armeabi-v7a/capicxx-core-runtime/lib/libCommonAPI.so
    │   │   ├── libCommonAPI-SomeIP.so -> /opt/thirdparty/android-29/armeabi-v7a/capicxx-someip-runtime/lib/libCommonAPI-SomeIP.so
    │   │   ├── libvsomeip3-cfg.so -> /opt/thirdparty/android-29/armeabi-v7a/vsomeip/lib/libvsomeip3-cfg.so
    │   │   ├── libvsomeip3-e2e.so -> /opt/thirdparty/android-29/armeabi-v7a/vsomeip/lib/libvsomeip3-e2e.so
    │   │   ├── libvsomeip3-sd.so -> /opt/thirdparty/android-29/armeabi-v7a/vsomeip/lib/libvsomeip3-sd.so
    │   │   └── libvsomeip3.so -> /opt/thirdparty/android-29/armeabi-v7a/vsomeip/lib/libvsomeip3.so
    │   ├── x86
    │   │   ├── libCommonAPI.so -> /opt/thirdparty/android-29/x86/capicxx-core-runtime/lib/libCommonAPI.so
    │   │   ├── libCommonAPI-SomeIP.so -> /opt/thirdparty/android-29/x86/capicxx-someip-runtime/lib/libCommonAPI-SomeIP.so
    │   │   ├── libvsomeip3-cfg.so -> /opt/thirdparty/android-29/x86/vsomeip/lib/libvsomeip3-cfg.so
    │   │   ├── libvsomeip3-e2e.so -> /opt/thirdparty/android-29/x86/vsomeip/lib/libvsomeip3-e2e.so
    │   │   ├── libvsomeip3-sd.so -> /opt/thirdparty/android-29/x86/vsomeip/lib/libvsomeip3-sd.so
    │   │   └── libvsomeip3.so -> /opt/thirdparty/android-29/x86/vsomeip/lib/libvsomeip3.so
    │   └── x86_64
    │       ├── libCommonAPI.so -> /opt/thirdparty/android-29/x86_64/capicxx-core-runtime/lib/libCommonAPI.so
    │       ├── libCommonAPI-SomeIP.so -> /opt/thirdparty/android-29/x86_64/capicxx-someip-runtime/lib/libCommonAPI-SomeIP.so
    │       ├── libvsomeip3-cfg.so -> /opt/thirdparty/android-29/x86_64/vsomeip/lib/libvsomeip3-cfg.so
    │       ├── libvsomeip3-e2e.so -> /opt/thirdparty/android-29/x86_64/vsomeip/lib/libvsomeip3-e2e.so
    │       ├── libvsomeip3-sd.so -> /opt/thirdparty/android-29/x86_64/vsomeip/lib/libvsomeip3-sd.so
    │       └── libvsomeip3.so -> /opt/thirdparty/android-29/x86_64/vsomeip/lib/libvsomeip3.so
    .

Upvotes: 0

Ali Maddi
Ali Maddi

Reputation: 339

To package the prebuilt library into your APK, you need to manually configure Gradle with the sourceSets block to include the path to your .so file. After building your APK, you can verify which libraries Gradle packages into your APK by using the APK Analyzer(Build>Analyze APK...).

android {
...
sourceSets {
    main {
        jniLibs.srcDirs 'imported-lib/src/', 'more-imported-libs/src/'
    }
}

for more information please see here "Include prebuilt native libraries"

Upvotes: 3

Alex Cohn
Alex Cohn

Reputation: 57203

The prebuilt libLibrary.so does not have SONAME. If for whatever reason you cannot rebuild this library with latest NDK, you may try the patchelf utility to add SONAME to existing binary.

Upvotes: 4

Nabin Bhandari
Nabin Bhandari

Reputation: 16419

You don't need to use CMake to use shared libraries (.so files). Just place the libraries inside the respective directories in src/main/jniLibs directory. Then you can load the libraries directly.

Android studio may show error in the native method declarations but, your app will work fine.

Upvotes: 1

Related Questions