3

0. Problem

I've create an Android library (aar) which contains some so files.

After successfully loaded the aar file in my Android application project, I try to load the so files (from the aar classes) but failed with a java.lang.UnsatisfiedLinkError.

I think the Android application is not looking inside the aar in order to load the so files, but looking at the lib folder which is inside the Android application.

(I know I can copy those shared-libraries directly in the android application, but I don't want to do that)

1. Create the Android library (aar)

build.gradle

    apply plugin: 'com.android.library'
    
    android {
        compileSdkVersion 29
    
        defaultConfig {
            minSdkVersion 21
            targetSdkVersion 29
            versionCode 1
            versionName "1.0"
    
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
            consumerProguardFiles 'consumer-rules.pro'
    
            externalNativeBuild {
                cmake {
                    cppFlags "-std=c++11"
                    abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
                }
            }
    
            packagingOptions {
                pickFirst 'lib/x86_64/libnanolcms.so'
                pickFirst 'lib/x86/libnanolcms.so'
                pickFirst 'lib/armeabi-v7a/libnanolcms.so'
                pickFirst 'lib/arm64-v8a/libnanolcms.so'
                pickFirst 'lib/x86_64/liblcms2.so'
                pickFirst 'lib/x86/liblcms2.so'
                pickFirst 'lib/armeabi-v7a/liblcms2.so'
                pickFirst 'lib/arm64-v8a/liblcms2.so'
            }
        }
    
    
        buildTypes {
            release {
                minifyEnabled false
                debuggable false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
                jniDebuggable = true
            }
            debug {
                minifyEnabled false
                debuggable false
                jniDebuggable = true
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
    
    
        externalNativeBuild {
            cmake {
                version '3.6.0'
                path "src/main/jni/CMakeLists.txt"
            }
        }
    
        sourceSets {
            main {
                jniLibs.srcDirs = ['src/main/jnilibs']
                jni.srcDirs = [] //disable automatic ndk-build call
            }
        }
    }
    
    repositories {
        flatDir {
            dirs 'libs'
        }
    
        // to be able to compile opencv
        maven { url "https://jitpack.io" }
    }
    
    dependencies {
        implementation fileTree(include: ['*.jar','*.so'], dir: 'libs')
        implementation 'androidx.appcompat:appcompat:1.1.0'
        testImplementation 'junit:junit:4.13'
        androidTestImplementation 'androidx.test.ext:junit:1.1.1'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
        implementation(name: 'openCV-3.3.1', ext: 'aar')
    
        implementation "com.getkeepsafe.relinker:relinker:1.4.0"
    }

code from the android library project

   if (useReLinker) {
        ReLinker.recursively().log(logcatLogger).loadLibrary(CXColorcatchManager.getInstance().getWeakContext().get(), libraryName, new ReLinker.LoadListener() {
                @Override
                public void success() {
                    CXLogger.verbose("Library " + libraryName + " successfully loaded");
                    libraryLoaderListener.onLibraryLoaded(true);
                }
                @Override
                public void failure(Throwable t) {
                    CXLogger.verbose("Unable to load " + libraryName + " library \n");
                    libraryLoaderListener.onLibraryLoaded(false);
                }
            });

        } else {
            try {
                System.loadLibrary(libraryName);
                libraryLoaderListener.onLibraryLoaded(true);
            } catch (Exception e) {
                CXLogger.verbose("Unable to load " + libraryName + " library \n " + e);
                libraryLoaderListener.onLibraryLoaded(false);
            }
        }
    }

As you can see on the following picture, the aar file contains the so files. They are in the jni folder which is the correct folder following the documentation

my-library.aar

enter image description here

2. Android application project

I created an Android application project, added the aar file as a dependency (it works fine). I call the Singleton class I created from the aar file, then I try to load the so file from library class using System.loadlibrary() or Relinker.

build.gradle

    apply plugin: 'com.android.application'

    android {
        compileSdkVersion 29
        defaultConfig {
            applicationId "com.colorix.spike.nano"
            minSdkVersion 21

            targetSdkVersion 29
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }

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

    dependencies {
        implementation 'androidx.appcompat:appcompat:1.1.0'
        implementation 'com.google.android.material:material:1.0.0'
        implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
        testImplementation 'junit:junit:4.13'
        androidTestImplementation 'androidx.test:runner:1.3.0-alpha03'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0-alpha03'

        // my library
        implementation files('libs/my_library.aar')
        implementation "com.getkeepsafe.relinker:relinker:1.4.0"
    }

Unfortunately, with System.loadlibrary() it doesn't find the library : java.lang.UnsatisfiedLinkError

Using Relinker, I use recursive() method in order to check every folder :

    V/ReLinkerInstance: [LibraryLoadingTask.log()-32]: Loading the library normally failed: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk"],nativeLibraryDirectories=[/data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/lib/x86, /system/lib]]] couldn't find "libnanolcms.so"
        at java.lang.Runtime.loadLibrary0(Runtime.java:1012)
        at java.lang.System.loadLibrary(System.java:1669)
        at com.getkeepsafe.relinker.SystemLibraryLoader.loadLibrary(SystemLibraryLoader.java:24)
        at com.getkeepsafe.relinker.ReLinkerInstance.loadLibraryInternal(ReLinkerInstance.java:163)
        at com.getkeepsafe.relinker.ReLinkerInstance.access$000(ReLinkerInstance.java:31)
        at com.getkeepsafe.relinker.ReLinkerInstance$1.run(ReLinkerInstance.java:142)
        at java.lang.Thread.run(Thread.java:764)
    V/ReLinkerInstance: [LibraryLoadingTask.log()-32]: nanolcms (null) was not loaded normally, re-linking...
    V/ReLinkerInstance: [LibraryLoadingTask.log()-32]: Looking for lib/x86/libnanolcms.so in APK     /data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    V/ReLinkerInstance: [LibraryLoadingTask.log()-32]: Looking for lib/armeabi-v7a/libnanolcms.so in APK /data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    [LibraryLoadingTask.log()-32]: Looking for lib/armeabi/libnanolcms.so in APK /data/app    /com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    [LibraryLoadingTask.log()-32]: Looking for lib/x86/libnanolcms.so in APK /data/app    /com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    [LibraryLoadingTask.log()-32]: Looking for lib/armeabi-v7a/libnanolcms.so in APK /data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    V/ReLinkerInstance: [LibraryLoadingTask.log()-32]: Looking for lib/armeabi/libnanolcms.so in APK /data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    [LibraryLoadingTask.log()-32]: Looking for lib/x86/libnanolcms.so in APK /data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    [LibraryLoadingTask.log()-32]: Looking for lib/armeabi-v7a/libnanolcms.so in APK /data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    V/ReLinkerInstance: [LibraryLoadingTask.log()-32]: Looking for lib/armeabi/libnanolcms.so in APK /data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    V/ReLinkerInstance: [LibraryLoadingTask.log()-32]: Looking for lib/x86/libnanolcms.so in APK /data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    [LibraryLoadingTask.log()-32]: Looking for lib/armeabi-v7a/libnanolcms.so in APK /data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    [LibraryLoadingTask.log()-32]: Looking for lib/armeabi/libnanolcms.so in APK /data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    V/ReLinkerInstance: [LibraryLoadingTask.log()-32]: Looking for lib/x86/libnanolcms.so in APK /data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    V/ReLinkerInstance: [LibraryLoadingTask.log()-32]: Looking for lib/armeabi-v7a/libnanolcms.so in APK /data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    V/ReLinkerInstance: [LibraryLoadingTask.log()-32]: Looking for lib/armeabi/libnanolcms.so in APK /data/app/com.colorix.spike.nano-YmXglPRjs_msSVzvsySnZA==/base.apk...
    W/orix.spike.nan: Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (light greylist, reflection)
    W/orix.spike.nan: Accessing hidden method Landroid/view /ViewGroup;->makeOptionalFitsSystemWindows()V (light greylist, reflection)
    V/ReLinkerInstance: [LibraryLoadingTask.failure()-54]: Unable to load nanolcms library 

As you can see, the library is not in lib/<architecture/... but inside the aar, which is in jni folder.

If I understand the android documentation, lib folder is the folder of the android application and not the folder of the android library (aar).

If someone knows how to load the so file which are inside the aar file without, please let me know. If you need more information, let me know.

Solution (but I don't want that)

I can copy the .so files in the jniLibs folder of the application. It will works properly. But that's no what I want.

Community
  • 1
  • 1
piratefache
  • 1,308
  • 11
  • 17

3 Answers3

3

I finally figure it out by myself.

The first problem comes from my cmakelists.txt

I create my-lib-1 from c sources files then I created my-lib-2 in order to link my-lib-1 with some cpp files for jniexport.

But the link to my-lib-1 was correctly set for the compilation time, but when I created my-lib-2 from my-lib-1 the reference to my-lib-1 was broken.

Using System.loadlibrary() method to load my-lib-2 raised an error : java.lang.UnsatisfiedLinkError saying that my-lib-1 (not my-lib-2) wasn't found. That's how I fix the first problem.

Solution :

# mylib1 - source files
file(GLOB SOURCES ${mylib1_source_DIR}/src/*.c)

# mylib1 - include header files
include_directories(${mylib1_source_DIR}/include/)

# compile and add our cpp files - Shared library
add_library(mylib2 SHARED ${SOURCES} mylib2.cpp)

# logger
find_library(log-lib log)

# include our header file
target_include_directories( mylib2 PRIVATE ${mylib1_source_DIR}/include )

# link our library (mylib2) with android and log libraries
target_link_libraries( mylib2 android ${log-lib})

The second problem comes from relinker

The library is compiled. I use Relinker to load my library, but Relinker is unable to find my-lib-2. Relinker is looking for the library in the wrong folder (lib folder instead of jni folder).

fyi : lib folder is the library folder for an android app jni folder is the library folder for an aar library

The reason is probably because Relinker is called from an android application, which mean it's going to look at the application native library folder and not inside the aar folder (which contains my-lib-2)

Solution : 1. Remove relinker 2. load library from my aar classes :

System.loadLibrary(my_lib_2);

That's it

Thanks @bruno for your help

piratefache
  • 1,308
  • 11
  • 17
1

To my mind, you can't load a so file inside an aar lib.

What you have to do is a wrapper in your aar to access .so features because your .so, I suppose, is loaded when aar is loaded.

Bruno
  • 3,872
  • 4
  • 20
  • 37
  • 1
    Thanks for your answer. I guess i've done that. I compiled the C library using cmakelists.txt and wrap it with my header (cpp file) in order to use the methods from the C library. Then the code from the aar file are using the code from the C library with JNIEXPORT. It is correct? – piratefache Jan 29 '20 at 13:37
  • Yes, that is. Here is a very small example : https://stackoverflow.com/questions/51613950/kotlin-ndk-and-c-interactions – Bruno Jan 29 '20 at 16:45
  • 1
    Actually I already done that. But the problem is (as shown in your example) I still need to use `System.loadLibrary()` method. So the problem still unfortunately the same. – piratefache Jan 30 '20 at 08:00
0

for me the problem was that I didnt use @aar suffix. So I hade to use:

implementation('lib:2.0.3@aar') {
    transitive = true
}
tonisives
  • 1,962
  • 1
  • 18
  • 17