无法从应用程序项目加载存储在库 (aar) 中的 .so 文件

Posted

技术标签:

【中文标题】无法从应用程序项目加载存储在库 (aar) 中的 .so 文件【英文标题】:Can't load .so file stored in a library (aar) from an application project 【发布时间】:2020-05-15 00:05:26 【问题描述】:

0。问题

我创建了一个包含一些 so 文件的 android 库 (aar)。

在我的 Android 应用程序项目中成功加载 aar 文件后,我尝试加载 so 文件(来自 aar 类)但失败并显示 java.lang.UnsatisfiedLinkError

我认为 Android 应用程序不是为了加载 so 文件而查看 aar 内部,而是查看 Android 应用程序内部的 lib 文件夹。

(我知道我可以直接在 android 应用程序中复制这些共享库,但我不想这样做)

1。创建 Android 库 (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"
    

来自 android 库项目的代码

   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);
            
        
    

如下图所示,aar 文件包含 so 文件。它们位于 jni 文件夹中,这是 the documentation 之后的正确文件夹

我的图书馆.aar

2。安卓应用项目

我创建了一个 Android 应用程序项目,将 aar 文件添加为依赖项(它工作正常)。 我调用从 aar 文件创建的 Singleton 类,然后尝试使用 System.loadlibrary() 或 Relinker 从库类加载 so 文件。

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"
    

不幸的是,System.loadlibrary() 找不到库:java.lang.UnsatisfiedLinkError

使用 Relinker,我使用 recursive() 方法来检查每个文件夹:

    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 

如您所见,该库不在lib/<architecture/... 中,而是在aar 中,位于jni 文件夹中。

如果我了解 android 文档,lib 文件夹是 android 应用程序的文件夹,而不是 android 库 (aar) 的文件夹。

如果有人知道如何加载 aar 文件中没有的 so 文件,请告诉我。如果您需要更多信息,请告诉我。

解决方案(但我不想要)

我可以复制应用程序 jniLibs 文件夹中的 .so 文件。它会正常工作。但这不是我想要的。

【问题讨论】:

【参考方案1】:

我终于自己弄明白了。

第一个问题来自我的cmakelists.txt

我从 c 源文件创建 my-lib-1,然后创建 my-lib-2 以便将 my-lib-1 与 jniexport 的一些 cpp 文件链接。

但是在编译时正确设置了指向 my-lib-1 的链接,但是当我从 my-lib-1 创建 my-lib-2 时,对 my-lib-1 的引用被破坏了。

使用 System.loadlibrary() 方法加载 my-lib-2 引发错误:java.lang.UnsatisfiedLinkError 说未找到 my-lib-1(不是 my-lib-2)。这就是我解决第一个问题的方法。

解决方案:

# 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)

第二个问题来自relinker

库已编译。我使用 Relinker 加载我的库,但 Relinker 无法找到 my-lib-2。 Relinker 正在错误文件夹(lib 文件夹而不是 jni 文件夹)中查找库。

fyi : lib 文件夹是 android 应用程序的库文件夹 jni 文件夹是 aar 库的库文件夹

原因可能是因为 Relinker 是从 android 应用程序调用的,这意味着它会查看应用程序原生库文件夹而不是 aar 文件夹(包含 my-lib-2)内

解决方案: 1.删​​除重新链接器 2. 从我的 aar 类加载库:

System.loadLibrary(my_lib_2);

就是这样

感谢@bruno 的帮助

【讨论】:

所以最后,如果你有aar,不要使用Relinker 谢谢大佬,开始使用NDK了,刚刚总结了构建环境和库查找!!【参考方案2】:

在我看来,你不能在 aar 库中加载 so 文件。

你需要做的是在你的 aar 中封装一个访问 .so 功能的包装器,因为我想你的 .so 是在加载 aar 时加载的。

【讨论】:

感谢您的回答。我想我已经做到了。我使用 cmakelists.txt 编译了 C 库,并将其与我的头文件(cpp 文件)一起包装,以便使用 C 库中的方法。然后,aar 文件中的代码将 C 库中的代码与 JNIEXPORT 一起使用。对吗? 是的,就是这样。这是一个非常小的例子:***.com/questions/51613950/… 其实我已经做到了。但问题是(如您的示例所示)我仍然需要使用System.loadLibrary() 方法。所以不幸的是问题还是一样。【参考方案3】:

对我来说,问题是我没有使用@aar 后缀。所以我不得不使用:

implementation('lib:2.0.3@aar') 
    transitive = true

【讨论】:

以上是关于无法从应用程序项目加载存储在库 (aar) 中的 .so 文件的主要内容,如果未能解决你的问题,请参考以下文章

是否可以将配置文件从客户端应用程序传递到库项目?

如何将 AAR 转换为 JAR

从.aar文件Android中提取代码

在库中使用 .Net Standard 1.4 和在应用程序中使用 .Net framework 4.6.1 时,无法加载文件 System.IO.FileSystem,Version=4.0.1.0

无法从 .aar 文件访问类

如何从 Gradle 设置 AAR 输出的名称