Search code examples
androidgradleshared-librariesandroid-libraryaar

Can't load .so file stored in a library (aar) from an application project


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.


Solution

  • 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