如何在Android Studio中包含*.so库?

161

我读了许多关于如何将一个*.so库添加到Android Studio的帖子,但是它们中的任何一个都没有起作用,特别是当涉及到文本内容:“这在较新的xxx( Android Studio、gradle等)上不起作用”时。

我们能重新开始吗?我有:

Android Studio 0.6.0

从项目结构中我看到:

SDK位置:

/usr/share/android-studio/data/sdk
/usr/lib/jvm/default-java

项目:

Gradle version 1.10
Android Plugin Version 0.11.+

模块/应用: 属性:

编译 SDK 版本 19 构建工具版本 19.1.0

依赖项:

{dir=libs, include=[*.jar]} Compile

{dir=libs, include=[*.so]}  Provided

m com.android.support: appcompat -v7:19.+   Compile

我已经得到了*.so文件的预编译版本,并且在演示应用程序中它们可以工作。我需要更改应用程序的源代码,因此我需要使用相同的*.so文件重新构建。


将位于 Android 项目外部的 .so 文件添加到项目中:https://dev59.com/favka4cB1Zd3GeqP1PMo - user1506104
请在此处查看答案:https://dev59.com/qlwY5IYBdhLWcg3wpJP_#54977264 - shizhen
9个回答

260

在Android Studio 1.0.2中添加.so库

  1. 在"src/main/"内创建文件夹"jniLibs"
  2. 将所有的.so库放到"src/main/jniLibs"文件夹里
  3. 文件夹结构应如下所示:
    |--app:
    |--|--src:
    |--|--|--main
    |--|--|--|--jniLibs
    |--|--|--|--|--armeabi
    |--|--|--|--|--|--.so文件
    |--|--|--|--|--x86
    |--|--|--|--|--|--.so文件
  4. 不需要额外的代码,只需同步项目并运行应用程序即可。

    参考资料
    https://github.com/commonsguy/sqlcipher-gradle/tree/master/src/main

7
这在2015年6月16日的Studio测试版中无法使用。 - bugfixr
8
这是正确的答案,适用于Android Studio 1.2.2。已经过检查和验证。 - Akhil Jain
5
使用 Android Studio 1.3.1。 - Jaime Hablutzel
5
对我有用,谢谢。在Android Studio 2.1.2上使用 :) - ShujatAli
9
能够与 Android Studio 3.2.1 兼容,太好了!但它仍然没有在任何地方得到记录!? - NorbertM
显示剩余15条评论

137

当前解决方案

创建文件夹project/app/src/main/jniLibs,然后将您的*.so文件放置在该位置内其对应的abi文件夹中。例如:

project/
├──libs/
|  └── *.jar       <-- if your library has jar files, they go here
├──src/
   └── main/
       ├── AndroidManifest.xml
       ├── java/
       └── jniLibs/ 
           ├── arm64-v8a/                       <-- ARM 64bit
           │   └── yourlib.so
           ├── armeabi-v7a/                     <-- ARM 32bit
           │   └── yourlib.so
           └── x86/                             <-- Intel 32bit
               └── yourlib.so

已过时的解决方案

将以下两个代码片段作为依赖项添加到您的模块 gradle.build 文件中:

compile fileTree(dir: "$buildDir/native-libs", include: 'native-libs.jar')
如何创建此自定义jar:
task nativeLibsToJar(type: Jar, description: 'create a jar archive of the native libs') {
    destinationDir file("$buildDir/native-libs")
    baseName 'native-libs'
    from fileTree(dir: 'libs', include: '**/*.so')
    into 'lib/'
}

tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn(nativeLibsToJar)
}

可以在相关问题中找到相同的答案:在Android Studio中将.so库包含在apk中


6
Compileд»»еЉҰе·Іиұ«еәѓз”ЁпәЊиҮ·ж”№з”ЁJavaCompileпә€жқӨи‡Ғз›ёе…із­”жҰ€пә‰гЂ‚ - Sergii
1
我应该把任务放在哪里? - masoud vali
3
请先尝试jniLibs文件夹解决方案。这些任务应该放在您的app/library gradle.build文件中。 - nenick
我的输出是一个静态文件,我已经尝试过了,但它没有被打包到apk中。 - Declan Nnadozie
如何在Activity中调用本地库方法 - Divyanshu
显示剩余5条评论

38

解决方案1:创建JniLibs文件夹

在您的应用程序中创建一个名为“jniLibs”的文件夹,并将包含*.so文件的文件夹放入其中。 “jniLibs”文件夹需要在与“Java”或“Assets”文件夹相同的文件夹中创建。

解决方案2:修改build.gradle文件

如果您不想创建新文件夹并将*.so文件放入libs文件夹中,也可以!

在这种情况下,只需将*.so文件添加到libs文件夹中(请遵循与解决方案1相同的结构,例如libs/armeabi/.so),并修改应用程序的build.gradle文件以添加jniLibs的源目录。

sourceSets {
    main {
        jniLibs.srcDirs = ["libs"]
    }
}

您将在此处获得更多解释,并配有截图以帮助您(步骤6):

http://blog.guillaumeagis.eu/setup-andengine-with-android-studio/

编辑:必须是jniLibs.srcDirs,而不是jni.srcDirs - 已编辑代码。该目录可以是指向项目目录之外的[相对]路径。


1
方案2对我不起作用。我遇到了一个构建错误:“在源集'main'上找不到属性'jni'。” - Greg Brown
1
秘密是:“'jniLibs'文件夹需要在与您的'Java'或'Assets'文件夹相同的文件夹中创建。”。谢谢! - Seraphim's
方法1允许我正确编译,第二个方法在AS中创建了一个“cpp”文件夹,并给出了有关缺少C++编译器的错误。 - fillobotto
解决方案2必须使用jniLibs.srcDirs而不是jni.srcDirs来指定本地库的位置(路径可以是相对或绝对的,甚至可以指向项目目录之外)。 - astraujums
对于解决方案2,您需要将“source Sets {”代码放置在“android {”部分下面。 - yennster
对我来说,将'src/main/jniLibs'添加到jniLibs.srcDirs是必要的,谢谢! - Summer Sun

27

*.so库在Android Studio中的应用

您需要在Android Studio项目的main文件夹下生成jniLibs文件夹,并将所有的.so文件放入其中。您也可以在build.gradle文件中添加以下代码:

compile fileTree(dir: 'libs', include: ['.jar','.so'])

这样做可以完美地工作。

|--app:

|--|--src:

|--|--|--main

|--|--|--|--jniLibs

|--|--|--|--|--armeabi

|--|--|--|--|--|--.so文件

这是项目结构。


4
compile fileTree(dir: 'libs', include: ['.jar','.so']) 中添加 .so 可解决我的问题。谢谢。 - BST Kaal
如果以下解决方案无效,尝试使用Android NDK r10e。 - Vineet Setia

11

这是我的 build.gradle 文件,请注意该行

jniLibs.srcDirs = ['libs']

这将包括将libs的*.so文件打包进apk中。

sourceSets {
    main {
        manifest.srcFile 'AndroidManifest.xml'
        java.srcDirs = ['src']
        resources.srcDirs = ['src']
        aidl.srcDirs = ['src']
        renderscript.srcDirs = ['src']
        res.srcDirs = ['res']
        assets.srcDirs = ['assets']
        jniLibs.srcDirs = ['libs']
    }

    // Move the tests to tests/java, tests/res, etc...
    instrumentTest.setRoot('tests')

    // Move the build types to build-types/<type>
    // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
    // This moves them out of them default location under src/<type>/... which would
    // conflict with src/ being used by the main source set.
    // Adding new build types or product flavors should be accompanied
    // by a similar customization.
    debug.setRoot('build-types/debug')
    release.setRoot('build-types/release')
}

7

Android NDK官方hello-libsCMake示例

https://github.com/googlesamples/android-ndk/tree/840858984e1bb8a7fab37c1b7c571efbe7d6eb75/hello-libs

在Ubuntu 17.10主机、Android Studio 3和Android SDK 26上已经测试通过,因此我强烈建议您以此为基础开展项目。

共享库称为libgperf,关键代码部分如下:

  • hello-libs/app/src/main/cpp/CMakeLists.txt:

    // -L
    add_library(lib_gperf SHARED IMPORTED)
    set_target_properties(lib_gperf PROPERTIES IMPORTED_LOCATION
              ${distribution_DIR}/gperf/lib/${ANDROID_ABI}/libgperf.so)
    
    // -I
    target_include_directories(hello-libs PRIVATE
                               ${distribution_DIR}/gperf/include)
    // -lgperf
    target_link_libraries(hello-libs
                          lib_gperf)
    
  • app/build.gradle:

    android {
        sourceSets {
            main {
                // let gradle pack the shared library into apk
                jniLibs.srcDirs = ['../distribution/gperf/lib']
    

    Then, if you look under /data/app on the device, libgperf.so will be there as well.

  • on C++ code, use: #include <gperf.h>

  • header location: hello-libs/distribution/gperf/include/gperf.h

  • lib location: distribution/gperf/lib/arm64-v8a/libgperf.so

  • If you only support some architectures, see: Gradle Build NDK target only ARM

这个例子中,Git跟踪预构建的共享库,但它也包含了实际构建这些库所需的构建系统:https://github.com/googlesamples/android-ndk/tree/840858984e1bb8a7fab37c1b7c571efbe7d6eb75/hello-libs/gen-libs。请注意,保留了HTML标签。

4

我曾使用打包在jar文件中的外部本地库依赖项来解决类似的问题。有时,这些与架构相关的库会全部打包到一个jar中,有时它们会被拆分成几个jar文件。因此,我编写了一些构建脚本来扫描jar依赖项的本地库,并将它们分类到正确的Android lib文件夹中。此外,这还提供了一种下载未在Maven仓库中找到的依赖项的方法,这对于在Android上使用JNA非常有用,因为并非所有本地库都在公共maven仓库中发布。

android {
    compileSdkVersion 23
    buildToolsVersion '24.0.0'

    lintOptions {
        abortOnError false
    }


    defaultConfig {
        applicationId "myappid"
        minSdkVersion 17
        targetSdkVersion 23
        versionCode 1
        versionName "1.0.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    sourceSets {
        main {
            jniLibs.srcDirs = ["src/main/jniLibs", "$buildDir/native-libs"]
        }
    }
}

def urlFile = { url, name ->
    File file = new File("$buildDir/download/${name}.jar")
    file.parentFile.mkdirs()
    if (!file.exists()) {
        new URL(url).withInputStream { downloadStream ->
            file.withOutputStream { fileOut ->
                fileOut << downloadStream
            }
        }
    }
    files(file.absolutePath)
}
dependencies {
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.3.0'
    compile 'com.android.support:design:23.3.0'
    compile 'net.java.dev.jna:jna:4.2.0'
    compile urlFile('https://github.com/java-native-access/jna/blob/4.2.2/lib/native/android-arm.jar?raw=true', 'jna-android-arm')
    compile urlFile('https://github.com/java-native-access/jna/blob/4.2.2/lib/native/android-armv7.jar?raw=true', 'jna-android-armv7')
    compile urlFile('https://github.com/java-native-access/jna/blob/4.2.2/lib/native/android-aarch64.jar?raw=true', 'jna-android-aarch64')
    compile urlFile('https://github.com/java-native-access/jna/blob/4.2.2/lib/native/android-x86.jar?raw=true', 'jna-android-x86')
    compile urlFile('https://github.com/java-native-access/jna/blob/4.2.2/lib/native/android-x86-64.jar?raw=true', 'jna-android-x86_64')
    compile urlFile('https://github.com/java-native-access/jna/blob/4.2.2/lib/native/android-mips.jar?raw=true', 'jna-android-mips')
    compile urlFile('https://github.com/java-native-access/jna/blob/4.2.2/lib/native/android-mips64.jar?raw=true', 'jna-android-mips64')
}
def safeCopy = { src, dst ->
    File fdst = new File(dst)
    fdst.parentFile.mkdirs()
    fdst.bytes = new File(src).bytes

}

def archFromName = { name ->
    switch (name) {
        case ~/.*android-(x86-64|x86_64|amd64).*/:
            return "x86_64"
        case ~/.*android-(i386|i686|x86).*/:
            return "x86"
        case ~/.*android-(arm64|aarch64).*/:
            return "arm64-v8a"
        case ~/.*android-(armhf|armv7|arm-v7|armeabi-v7).*/:
            return "armeabi-v7a"
        case ~/.*android-(arm).*/:
            return "armeabi"
        case ~/.*android-(mips).*/:
            return "mips"
        case ~/.*android-(mips64).*/:
            return "mips64"
        default:
            return null
    }
}

task extractNatives << {
    project.configurations.compile.each { dep ->
        println "Scanning ${dep.name} for native libs"
        if (!dep.name.endsWith(".jar"))
            return
        zipTree(dep).visit { zDetail ->
            if (!zDetail.name.endsWith(".so"))
                return
            print "\tFound ${zDetail.name}"
            String arch = archFromName(zDetail.toString())
            if(arch != null){
                println " -> $arch"
                safeCopy(zDetail.file.absolutePath,
                        "$buildDir/native-libs/$arch/${zDetail.file.name}")
            } else {
                println " -> No valid arch"
            }
        }
    }
}

preBuild.dependsOn(['extractNatives'])

3
我使用的是Android Studio 4.1.2版本,在文件菜单 -> 项目结构 -> SDK位置中设置NDK路径时,即使它已经安装好了,也会要求我下载NDK。我设置了NDK_HOME,ANDROID_NDK_HOME和PATH环境变量,但都没有用。仅有的解决方法是在local.properties文件中手动设置NDK版本,并创建一个名为"jniLibs"(确切名称)的文件夹,将支持的ABI及其相应的.so文件放入其中,并在build.gradle中指定ABI过滤器和NDK版本。希望这能帮助其他人避免麻烦。 local.properties
ndk.dir=C\:\\Users\\<user>\\AppData\\Local\\Android\\Sdk\\ndk\\22.0.7026061

build.gradle

plugins {
    id 'com.android.application'
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.example.myapplication"
        minSdkVersion 22
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
        ndk.abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

    }

    sourceSets {
        main {
            jniLibs.srcDir 'jniLibs'
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    ndkVersion '22.0.7026061'
}

位于jniLibs文件夹内

输入图像描述

位于\app\src\main\文件夹内

输入图像描述


1

要使用本地库(so文件),您需要在“build.gradle”文件中添加一些代码。

此代码用于在“清理项目”时清除“armeabi”目录并将“so”文件复制到“armeabi”中。

task copyJniLibs(type: Copy) {
    from 'libs/armeabi'
    into 'src/main/jniLibs/armeabi'
}
tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn(copyJniLibs)
}
clean.dependsOn 'cleanCopyJniLibs'

我是从以下链接转介而来。 https://gist.github.com/pocmo/6461138


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接