Jak dołączyć bibliotekę * .so do Android Studio?

123

Czytałem wiele wątków, jak dodać bibliotekę * .so do Android Studio, ale żaden z nich nie działa, szczególnie jeśli chodzi o treść: To nie działa z nowszym xxx (Android Studio, gradle, ...)

Czy możemy zacząć od nowa? Mam:

Android Studio 0.6.0

Ze struktury projektu widzę:

Lokalizacja SDK:

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

Projekt:

Gradle version 1.10
Android Plugin Version 0.11.+

Moduły / aplikacja: Właściwości:

Kompiluj Sdk w wersji 19 Narzędzia do budowania w wersji 19.1.0

Zależności:

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

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

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

Mam wstępnie skompilowane pliki * .so i działają w aplikacji demonstracyjnej. Muszę zmienić kod źródłowy aplikacji, więc muszę przebudować z tymi samymi plikami * .so.

Ronald Wiplinger
źródło
dodaj plik .so z katalogu poza projektem androida: stackoverflow.com/questions/50713933/ ...
user1506104
Sprawdź odpowiedź tutaj: stackoverflow.com/a/54977264/8034839
shizhen

Odpowiedzi:

108

Aktualne rozwiązanie

Utwórz folder project/app/src/main/jniLibs, a następnie umieść *.sopliki w ich folderach abi w tej lokalizacji. Na przykład,

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

Przestarzałe rozwiązanie

Dodaj oba fragmenty kodu w pliku gradle.build modułu jako zależność:

compile fileTree(dir: "$buildDir/native-libs", include: 'native-libs.jar')

Jak stworzyć ten niestandardowy słoik:

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

Tę samą odpowiedź można również znaleźć w powiązanym pytaniu: Dołącz bibliotekę .so do apk w Android Studio

nenick
źródło
6
Compilezadanie zostało wycofane. Użyj JavaCompilezamiast tego (z powiązanych odpowiedzi)
Sergii
gdzie powinienem umieścić zadania?
masoud vali,
2
Najpierw wypróbuj rozwiązanie dotyczące folderu jniLibs. Zadania te należy umieścić w pliku gradle.build aplikacji / biblioteki.
nenick
Odniesienie do jniLibsścieżki katalogu w Podręczniku użytkownika wtyczki Gradle - Struktura projektu
Eido95
zobacz także tutaj (zawiera listę różnych podfolderów architektury): cumulations.com/blogs/9/…
NorbertM
223

Dodanie biblioteki .so w Android Studio 1.0.2

  1. Utwórz folder „jniLibs” wewnątrz „src / main /”
  2. Umieść wszystkie swoje biblioteki .so w folderze „src / main / jniLibs”
  3. Wygląda
    następująco: | --app:
    | - | --src:
    | - | - | --main
    | - | - | - | --jniLibs
    | - | - | - | - | --armeabi
    | - | - | - | - | - | -. so Pliki
    | - | - | - | - | --x86
    | - | - | - | - | - | -. so Pliki
  4. Żaden dodatkowy kod nie wymaga jedynie zsynchronizowania projektu i uruchomienia aplikacji.

    Odniesienia
    https://github.com/commonsguy/sqlcipher-gradle/tree/master/src/main
Vasanth
źródło
6
To nie działa w wersji beta programu Studio z 16 czerwca 2015 r.
bugfixr
6
to jest prawidłowa odpowiedź, działająca w Android Studio 1.2.2. sprawdzone i zweryfikowane.
Akhil Jain
3
Praca z Android Studio 1.3.1.
Jaime Hablutzel
3
Pracowało dla mnie dzięki. na Android Studio 2.1.2 :)
ShujatAli
3
działa z Androidem Studio 3.2.1, świetnie! Nadal nigdzie nie udokumentowane! ???
NorbertM
29

Rozwiązanie 1: Utworzenie folderu JniLibs

Utwórz folder o nazwie „jniLibs” w swojej aplikacji i foldery zawierające plik * .so w środku. Folder „jniLibs” należy utworzyć w tym samym folderze, co foldery „Java” lub „Assets”.

Rozwiązanie 2: Modyfikacja pliku build.gradle

Jeśli nie chcesz tworzyć nowego folderu i przechowywać swoje pliki * .so w folderze libs, jest to możliwe!

W takim przypadku po prostu dodaj swoje pliki * .so do folderu libs (przestrzegaj tej samej architektury, co rozwiązanie 1: na przykład libs / armeabi / .so) i zmodyfikuj plik build.gradle swojej aplikacji, aby dodać katalog źródłowy z jniLibs.

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

Będziesz mieć więcej wyjaśnień, z pomocnymi zrzutami ekranu (krok 6):

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

EDIT Musiało to być jniLibs.srcDirs, a nie jni.srcDirs - edytowano kod. Katalog może być ścieżką [względną], która wskazuje poza katalog projektu.

GuillaumeAgis
źródło
1
Rozwiązanie 2 nie działa dla mnie. Pojawia się błąd kompilacji: „Nie można znaleźć właściwości 'jni' w zestawie źródeł 'main'.”
Greg Brown
Sekretem było „Folder„ jniLibs ”należy utworzyć w tym samym folderze co foldery„ Java ”lub„ Zasoby ”.”. Dzięki!
Seraphim's
Metoda 1 pozwoliła mi poprawnie skompilować, druga utworzyła folder "cpp" w AS i dała mi błąd o braku kompilatora C ++
fillobotto
Rozwiązanie 2 musiało użyć jniLibs.srcDirs, a nie jni.srcDirs, aby umożliwić określenie lokalizacji bibliotek natywnych (ścieżka może być względna lub bezwzględna i może wskazywać nawet poza katalog projektu).
astraujums
Aby uzyskać rozwiązanie 2, musisz umieścić source Sets {kod w android {sekcji
yennster
26

Biblioteka * .so w Android Studio

Musisz wygenerować folder jniLibs w głównym projekcie Android Studio i umieścić w nim wszystkie pliki .so. Możesz również zintegrować tę linię w build.gradle

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

Działa idealnie

| --app:

| - | --src:

| - | - | --main

| - | - | - | --jniLibs

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

| - | - | - | - | - | -. so Pliki

Taka jest struktura projektu.

Ramkailash
źródło
4
Dodanie .so do kompilacji fileTree (dir: 'libs', include: ['.jar', '. So']) rozwiązało mój PRB. thnx
BST Kaal
Jeśli nadal jest po poniższym rozwiązaniu, wypróbuj system Android ndk r10e
Vineet Setia
12

To jest mój plik build.gradle. Zwróć uwagę na linię

jniLibs.srcDirs = ['libs']

Będzie to obejmować plik * .so biblioteki libs do 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')
}
NovemberEleven
źródło
5

Oficjalny hello-libsprzykład CMake dla Androida NDK

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

Po prostu pracował dla mnie na hoście Ubuntu 17.10, Android Studio 3, Android SDK 26, więc zdecydowanie polecam oprzeć swój projekt na nim.

Biblioteka współdzielona jest nazywana libgperf, kluczowe części kodu to:

  • 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']

    Wtedy, jeśli spojrzysz pod /data/appurządzenie, libgperf.sorównież tam będzie.

  • w kodzie C ++ użyj: #include <gperf.h>

  • lokalizacja nagłówka: hello-libs/distribution/gperf/include/gperf.h

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

  • Jeśli obsługujesz tylko niektóre architektury, zobacz: Gradle Build NDK jest przeznaczony tylko dla ARM

Przykładowy git śledzi wstępnie skompilowane biblioteki współdzielone, ale zawiera również system kompilacji do ich tworzenia: https://github.com/googlesamples/android-ndk/tree/840858984e1bb8a7fab37c1b7c571efbe7d6eb75/hello-libs/gen-libs

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
2

Aby korzystać z biblioteki natywnej (plików so), musisz dodać kilka kodów w pliku „build.gradle”.

Ten kod służy do czyszczenia katalogu „armeabi” i kopiowania plików „so” do „armeabi” podczas „czyszczenia projektu”.

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

Zostałem skierowany z dołu. https://gist.github.com/pocmo/6461138

ładna angela
źródło
2

Rozwiązałem podobny problem, używając zewnętrznych natywnych zależności lib, które są spakowane w plikach jar. Czasami te biblioteki zależne od architektury są pakowane razem w jednym pliku jar, czasami są podzielone na kilka plików jar. więc napisałem skrypt kompilacyjny, aby przeskanować zależności jar dla natywnych bibliotek i posortować je do odpowiednich folderów bibliotek Androida. Dodatkowo zapewnia to również sposób pobierania zależności, których nie ma w repozytoriach maven, co jest obecnie przydatne, aby JNA działało na Androidzie, ponieważ nie wszystkie natywne pliki JAR są publikowane w publicznych repozytoriach 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'])
Jan
źródło
0

Wypróbowałem rozwiązania z powyższych odpowiedzi, ale żadne nie zadziałało. Miałem bibliotekę z plikami .so, .dll i .jar. Na koniec zrobiłem to, szczegóły możesz zobaczyć tutaj: https://stackoverflow.com/a/54976458/7392868

Skopiowałem wklejone pliki .so do folderu o nazwie jniLibs i wkleiłem je do folderu app / src / main /. W przypadku innych zależności użyłem zależności klas.

Muhammad Ali
źródło