简体   繁体   中英

Using Pre-built Shared Library in Android Studio

I need to use a custom prebuilt shared library (built on standalone ndk as libdynamic.so) in my android project. I created a folder "jniLibs" in path src/main and then 4 folders inside that namely "armeabi" "armeabi-v7a" "x86" "x86_64". I have put the prebuilt library inside all these 4 folders.

Now from my native code I want to call a function of this library. In the following way (included header in cmakelists.txt):

extern "C"
JNIEXPORT jstring JNICALL
Java_demo_co_ru_jnilibtest_MainActivity_stringFromJNI(
    JNIEnv *env,
    jobject /* this */) {

     float inv = rsqrt(3);  //FUNCTION FROM LIBRARY (libdynamic.so)

     std::string hello = "Hello ";
     return env->NewStringUTF(hello.c_str());
}

I get following errors:

  1. Error:error: cannot find -ldynamic

  2. Error:(19) undefined reference to 'rsqrt(float)'

  3. Error:error: linker command failed with exit code 1 (use -v to see invocation)

It seems that shared library is not getting located. I entered following values in CMakeLists.txt

include_directories( src/main/cpp/include) #include header of libdynamic.so
target_link_libraries(native-lib dynamic)  #dependency of native-lib on libdynamic.so

I added following additional entries inside my gradle build (app):

defaultConfig {
  ndk{
        abiFilters 'armeabi', 'armeabi-v7a', 'x86', 'x86_64'
    }
}

sourceSets {
    main {
        jni.srcDirs = ['src/main/jni', 'src/main/jniLibs/']
    }
}


externalNativeBuild {
    cmake {
        path "CMakeLists.txt"
    }
}

I am able to successfully run the library using android push and android shell. It is the apk build using Android Studio that is causing problem. I am using Android Studio version 2.3.3. Any help is highly appreciated.

In order to load your library with CMake in Android environment you will have to add the following code in native-lib CMakeLists.txt:

Set your libs path

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

Add library to import

add_library(DYNAMIC_LIB SHARED IMPORTED)

Set library location for each ABI

set_target_properties(DYNAMIC_LIB PROPERTIES IMPORTED_LOCATION ${LIBS_DIR}/${ANDROID_ABI}/lidynamic.so)

Link imported library to target

target_link_libraries(native-lib DYNAMIC_LIB)

and in the native-lib build.gradle:

defaultConfig{
    ...
    externalNativeBuild{
        // Specify the toolchain which was used to cross compile libdynamic.so because Android Studio assumes that you used clang
        arguments '-DANDROID_TOOLCHAIN=gcc'
    }
}

Add the sysroot lib dir to LDFLAGS using -L since if I recall correctly libdynamic also relies on libc, libdl, and should require libm at the very least.

The path should be:

$NDK/platforms/android-(platform-version)/arch-(architecture)/usr/lib

I was able to make it work using Android.mk instead of cmake. I am posting configurations and contents of Android.mk and gradle build just in case any one needs it.

Create a folder "jni" under "app". Create another custom folder "yourlibs" and put all your pre-built libs inside this "yourlibs" folder in respective "TARGET_ARCH_ABI" folder. For Example, in my case:

  • jni/yourlibs/armeabi/libdynamic.so
  • jni/yourlibs/armeabi-v7a/libdynamic.so
  • jni/yourlibs/x86/libdynamic.so
  • jni/yourlibs/x86_64/libdynamic.so

Now follow these steps:

  1. Create a "C" file inside the "jni" folder from where you would call the function defined inside the "libdynamic.so". Add neccesary header files to your created "C" file. For me it is "uselib.c" and "header.h"
  2. Create a file named "Android.mk" inside the "jni" folder

Add following contents in Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := yourlibs/$(TARGET_ARCH_ABI)/libdynamic.so
LOCAL_MODULE := add_prebuilt
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_SRC_FILES  := uselib.c
LOCAL_MODULE     :=  use-lib
LOCAL_SHARED_LIBRARIES := add_prebuilt
include $(BUILD_SHARED_LIBRARY)

Update gradle build (app) file to use "Android.mk" instead of cmake:

Inside "android => defaultConfig"

 ndk{
        abiFilters 'armeabi', 'armeabi-v7a', 'x86', 'x86_64'
    }

Inside "android"

externalNativeBuild {
    ndkBuild {
        path "jni/Android.mk"
    }
}

This will make a library called "use-lib" that uses "libdynamic.so" and it will pack both the libraries inside the lib folder of apk. You can check this using apk analyser (Android Studio => Build => Analyse Apk ...). To use "use-lib" use jni call, like:

static {
    System.loadLibrary("use-lib");
}

public native String stringFromJNI(); 

Note: I removed extern "C" statement from the C code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM