วิธีรวมไลบรารี * .so ใน Android Studio


123

ฉันอ่านหลายหัวข้อเกี่ยวกับการเพิ่มไลบรารี * .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 Build Tools เวอร์ชัน 19.1.0

อ้างอิง:

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

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

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

ฉันได้รับไฟล์ * .so ที่คอมไพล์ไว้ล่วงหน้าและที่แอพสาธิตพวกเขาใช้งานได้ ฉันต้องเปลี่ยนซอร์สโค้ดของแอพดังนั้นฉันต้องสร้างใหม่ด้วยไฟล์ * .so เดียวกัน


เพิ่มไฟล์. so จากไดเรกทอรีภายนอกโครงการ android: stackoverflow.com/questions/50713933/…
user1506104

ตรวจสอบคำตอบที่นี่: stackoverflow.com/a/54977264/8034839
shizhen

คำตอบ:


108

โซลูชันปัจจุบัน

สร้างโฟลเดอร์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)
}

คำตอบเดียวกันนี้สามารถพบได้ในคำถามที่เกี่ยวข้อง: รวม. so library ใน apk ใน android studio


6
Compileเลิกใช้งานแล้ว ใช้JavaCompileแทน (จากคำตอบที่เกี่ยวข้อง)
Sergii

ฉันควรวางงานไว้ที่ไหน
masoud vali

2
โปรดลองใช้โซลูชันโฟลเดอร์ jniLibs ก่อน งานนี้ควรใส่ไว้ในไฟล์ gradle.build app / library ของคุณ
nenick

อ้างอิงjniLibsเส้นทางไดเร็กทอรีที่Gradle Plugin User Guide - Project Structure
Eido95

ดูได้ที่นี่ (แสดงรายการโฟลเดอร์ย่อยสถาปัตยกรรมต่างๆ): cumulations.com/blogs/9/…
NorbertM

222

การเพิ่ม. so Library ใน Android Studio 1.0.2

  1. สร้างโฟลเดอร์ "jniLibs" ภายใน "src / main /"
  2. วางไลบรารี. so ทั้งหมดของคุณไว้ในโฟลเดอร์ "src / main / jniLibs"
  3. โครงสร้างโฟลเดอร์มีลักษณะดังนี้
    | --app:
    | - | --src:
    | - | - | - หลัก
    | - | - | - | --jniLibs
    | - | - | - | - | --armeabi
    | - | - | - | - | - | -. ดังนั้นไฟล์
    | - | - | - | - | --x86
    | - | - | - | - | - | -. ดังนั้นไฟล์
  4. ไม่ต้องใช้รหัสพิเศษเพียงแค่ซิงค์โครงการของคุณและเรียกใช้แอปพลิเคชันของคุณ

    อ้างอิง
    https://github.com/commonsguy/sqlcipher-gradle/tree/master/src/main

6
สิ่งนี้ใช้ไม่ได้กับ Studio เวอร์ชันเบต้า 16 มิถุนายน 2015
bugfixr

6
นี่คือคำตอบที่ถูกต้องทำงานใน Android Studio 1.2.2 ตรวจสอบและตรวจสอบแล้ว
Akhil Jain

3
การทำงานกับ Android Studio 1.3.1
Jaime Hablutzel

3
ทำงานให้ฉันขอบคุณ บน android studio 2.1.2 :)
ShujatAli

3
ใช้งานได้กับ Android Studio 3.2.1 เยี่ยมมาก! ยังไม่มีเอกสารที่ไหน! ???
NorbertM

29

โซลูชันที่ 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 ไม่ได้ผลสำหรับฉัน ฉันได้รับข้อผิดพลาดในการสร้าง: "ไม่พบคุณสมบัติ" jni "ในชุดแหล่งที่มา" main ""
Greg Brown

ความลับคือ "โฟลเดอร์" jniLibs "ต้องสร้างในโฟลเดอร์เดียวกับโฟลเดอร์" Java "หรือ" Assets "ของคุณ ขอบคุณ!
Seraphim

วิธีที่ 1 ทำให้ฉันสามารถคอมไพล์ได้อย่างถูกต้องอันที่สองสร้างโฟลเดอร์ "cpp" ใน AS และทำให้ฉันมีข้อผิดพลาดเกี่ยวกับคอมไพเลอร์ C ++ ที่หายไป
fillobotto

โซลูชันที่ 2 ต้องใช้ jniLibs.srcDirs ไม่ใช่ jni.srcDirs เพื่ออนุญาตให้ระบุตำแหน่งของไลบรารีเนทีฟ (พา ธ สามารถเป็นแบบสัมพัทธ์หรือแบบสัมบูรณ์และสามารถชี้ได้แม้จะอยู่นอกไดเร็กทอรีโปรเจ็กต์)
astraujums

สำหรับโซลูชันที่ 2 คุณต้องวางsource Sets {รหัสใต้android {ส่วน
yennster

26

* .so ไลบรารีใน Android Studio

คุณต้องสร้างโฟลเดอร์ jniLibs ภายในโครงการหลักของ Android Studio และใส่ไฟล์. so ทั้งหมดของคุณไว้ภายใน คุณยังสามารถรวมบรรทัดนี้ใน build.gradle

คอมไพล์ fileTree (dir: 'libs', include: [' .jar', ' .so'])

มันทำงานได้อย่างสมบูรณ์

| --app:

| - | --src:

| - | - | --main

| - | - | - | --jniLibs

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

| - | - | - | - | - | -. ดังนั้นไฟล์

นี่คือโครงสร้างโครงการ


4
การเพิ่ม. ดังนั้นในการคอมไพล์ fileTree (dir: 'libs', include: ['.jar', '. so'])แก้ไข prb ของฉัน thnx
BST Kaal

หากยังคงเป็นหลังจากวิธีแก้ปัญหาด้านล่างลองใช้ android ndk r10e
Vineet Setia

12

นี่คือไฟล์ build.gradle ของฉันโปรดสังเกตบรรทัด

jniLibs.srcDirs = ['libs']

ซึ่งจะรวมไฟล์ * .so ของ libs ไปยัง 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')
}

5

hello-libsตัวอย่าง CMake อย่างเป็นทางการของ Android NDK

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

เพิ่งทำงานให้ฉันบนโฮสต์ Ubuntu 17.10, Android Studio 3, Android SDK 26 ดังนั้นฉันขอแนะนำให้คุณใช้โครงการของคุณเป็นพื้นฐาน

ไลบรารีที่ใช้ร่วมกันเรียกว่าlibgperfส่วนของรหัสสำคัญคือ:

  • สวัสดี 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)
  • แอป / build.gradle :

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

    จากนั้นหากคุณมอง/data/appไปที่อุปกรณ์libgperf.soก็จะอยู่ที่นั่นเช่นกัน

  • ในรหัส C ++ ให้ใช้: #include <gperf.h>

  • ตำแหน่งส่วนหัว: hello-libs/distribution/gperf/include/gperf.h

  • ตำแหน่ง lib: distribution/gperf/lib/arm64-v8a/libgperf.so

  • หากคุณสนับสนุนเฉพาะสถาปัตยกรรมบางอย่างโปรดดู: Gradle Build NDK กำหนดเป้าหมายเฉพาะ ARM

git ตัวอย่างติดตามไลบรารีที่ใช้ร่วมกันที่สร้างไว้ล่วงหน้า แต่ยังมีระบบการสร้างเพื่อสร้างจริงด้วยเช่นกัน: https://github.com/googlesamples/android-ndk/tree/840858984e1bb8a7fab37c1b7c571efbe7d6eb75/hello-libs/gen-libs


2

ในการใช้ไลบรารีดั้งเดิม (ดังนั้นไฟล์) คุณต้องเพิ่มรหัสในไฟล์ "build.gradle"

รหัสนี้ใช้สำหรับล้างไดเรกทอรี "armeabi" และคัดลอกไฟล์ "so" ไปยัง "armeabi" ในขณะที่ "clean project"

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


2

ฉันได้แก้ไขปัญหาที่คล้ายกันโดยใช้การอ้างอิง lib เนทีฟภายนอกที่บรรจุอยู่ในไฟล์ jar บางครั้งสถาปัตยกรรมเหล่านี้ขึ้นอยู่กับไลบรารีจะรวมกันเป็นแพ็กเกจทั้งหมดภายในโถเดียวบางครั้งก็แยกออกเป็นไฟล์ jar หลายไฟล์ ดังนั้นฉันจึงเขียน buildscript เพื่อสแกนการขึ้นต่อกันของ jar สำหรับ libs ดั้งเดิมและเรียงลำดับลงในโฟลเดอร์ lib ของ android ที่ถูกต้อง นอกจากนี้ยังมีวิธีดาวน์โหลดการอ้างอิงที่ไม่พบใน maven repos ซึ่งปัจจุบันมีประโยชน์อย่างยิ่งในการทำให้ JNA ทำงานบน Android เนื่องจากไม่ได้เผยแพร่ขวดดั้งเดิมทั้งหมดใน repos 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'])

0

ฉันลองวิธีแก้ปัญหาในคำตอบข้างต้นแล้ว แต่ไม่มีผลกับฉัน ฉันมีไลบรารีที่มีไฟล์. so, .dll และ. jar ในตอนท้ายฉันทำสิ่งนี้คุณสามารถดูรายละเอียดได้ที่นี่: https://stackoverflow.com/a/54976458/7392868

ฉันคัดลอกวางไฟล์. so ในโฟลเดอร์ชื่อ jniLibs และวางลงใน app / src / main / folder สำหรับการอ้างอิงอื่น ๆ ฉันใช้การอ้างอิงเกรด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.