System.loadLibrary (…) nie może znaleźć biblioteki natywnej w moim przypadku

91

Chcę użyć istniejącej biblioteki natywnej z innego projektu systemu Android, więc właśnie skopiowałem wbudowaną bibliotekę NDK ( libcalculate.so ) do mojego nowego projektu Androida. W moim nowym projekcie na Androida utworzyłem folder libs/armeabi/i umieściłem tam libcalculate.so . Nie ma folderu jni /. Moje urządzenie testowe ma architekturę ARM.

W moim kodzie java ładuję bibliotekę przez:

  static{
    System.loadLibrary("calculate");
  }

Kiedy uruchamiam nowy projekt na Androida, pojawia się błąd:

java.lang.UnsatisfiedLinkError:  ...
nativeLibraryDirectories=[/vendor/lib, /system/lib]]] couldn't find "libcalculate.so"

Tak więc, jak mówi błąd, skopiowanej biblioteki natywnej nie ma w / verdor / lib ani / system / lib, jak rozwiązać ten problem w moim przypadku?

(Rozpakowałem pakiet apk, pod lib / jest libcalculate.so)

==== UPDATE =====

Próbowałem również utworzyć folder jni / w katalogu głównym projektu i dodać plik Android.mk w katalogu jni /. Zawartość Android.mk to:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := libcalculate.so
include $(PREBUILT_SHARED_LIBRARY)

Następnie w katalogu głównym projektu wykonałem ndk-build. Następnie katalogi armeabi / i armeabi-v7a / są generowane przez ndk-build (z libcalculate.so w folderze).

Następnie pomyślnie uruchomiłem mojego mavena, aby zbudować projekt. W ostatecznym pakiecie apk znajdują się:

lib/armeabi/libcalculate.so
lib/armeabi-v7a/libcalculate.so

Ale kiedy uruchamiam moją aplikację, ten sam błąd:

java.lang.UnsatisfiedLinkError:  ...
nativeLibraryDirectories=[/vendor/lib, /system/lib]]] couldn't find "libcalculate.so"
user842225
źródło
3
Umieściłeś bibliotekę bezpośrednio pod libs/? Prawdopodobnie musisz utworzyć jeden podkatalog dla każdego docelowego ABI, który chcesz obsługiwać (armeabi, armeabi-v7a, x86, mips, itp.) I umieścić odpowiedni plik .so w każdym podkatalogu (tj. Plik .so zbudowany dla armeabi trafia libs/armeabi/, itp).
Michael,
@Michael, właśnie przegapiłem to w moim poście, umieściłem to pod libs / armeabi /
user842225
Sprawdź, czy plik libcalculate.so jest faktycznie pobierany przez proces pakowania - spróbuj np. unzip -l package.apkLub zmień nazwę apk na .zip i otwórz go w jakiejś aplikacji. Jeśli go tam nie ma, coś jest nie tak z pakowaniem go (czy twoje IDE zauważyło, że folder tam jest, czy musisz odświeżyć projekt?).
mstorsjo,
@mstorsjo, rozpakowałem pakiet apk, pod lib / jest libcalculate.so
user842225
1
nie musisz mieć pliku Android.mk ani żadnych plików związanych z kompilacją. Po prostu umieść pliki so w odpowiednich podkatalogach do jniLibs, takich jak tutaj: github.com/Ph1b/MaterialAudiobookPlayer/tree/master/audiobook/ ...
Paul Woitaschek

Odpowiedzi:

177

Aby usunąć przyczynę (i być może w tym samym czasie, rozwiązać problem), oto co możesz zrobić:

  1. Usuń folder jni i wszystkie pliki .mk . Nie potrzebujesz ich ani NDK, jeśli niczego nie kompilujesz.

  2. Skopiuj libcalculate.soplik do środka <project>/libs/(armeabi|armeabi-v7a|x86|...). Podczas korzystania z Android Studio jest <project>/app/src/main/jniLibs/(armeabi|armeabi-v7a|x86|...), ale widzę, że używasz zaćmienia.

  3. Zbuduj swój plik APK i otwórz go jako plik zip , aby sprawdzić, czy libcalculate.soplik znajduje się w bibliotece lib / (armeabi | armeabi-v7a | x86 | ...) .

  4. Usuń i zainstaluj aplikację

  5. Uruchom pakiety pakietów dumpsys | grep yourpackagename, aby uzyskać nativeLibraryPath lub legacyNativeLibraryDir swojej aplikacji.

  6. Uruchom ls na nativeLibraryPath , który miałeś, lub na legacyNativeLibraryDir / armeabi , aby sprawdzić, czy plik libcalculate.so rzeczywiście tam jest.

  7. Jeśli tam jest, sprawdź, czy nie został zmieniony w stosunku do oryginalnego pliku libcalculate.so : czy jest skompilowany z odpowiednią architekturą, czy zawiera oczekiwane symbole, czy brakuje jakichkolwiek zależności. Możesz analizować libcalculate.so używając readelf.

Aby sprawdzić kroki 5-7, możesz użyć mojej aplikacji zamiast linii poleceń i odczytać: Native Libs Monitor

PS: Łatwo się pomylić, gdzie domyślnie powinny być umieszczane lub generowane pliki .so, oto podsumowanie:

  • libs / CPU_ABI wewnątrz projektu eclipse

  • jniLibs / CPU_ABI wewnątrz projektu Android Studio

  • jni / CPU_ABI wewnątrz AAR

  • lib / CPU_ABI wewnątrz ostatecznego pliku APK

  • wewnątrz nativeLibraryPath aplikacji na urządzeniu <5,0 i wewnątrz legacyNativeLibraryDir / CPU_ARCH aplikacji na urządzeniu> = 5.0.

Gdzie CPU_ABI to dowolne z: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64 . W zależności od wybranych architektur i dla których zostały skompilowane biblioteki.

Należy również zauważyć, że nie są pomieszane libs między katalogów CPU_ABI: trzeba cały zestaw co używasz, lib, który jest wewnątrz armeabi folderu nie zostanie zainstalowany na armeabi-v7a urządzenia, czy są jakieś libs wewnątrz armeabi -v7a z pliku APK.

ph0b
źródło
3
Wspaniały człowiek, dzięki! Używam Android Studio i moje kompilacje jni były kopiowane do bibliotek zamiast jniLibs.
Luis
4
Ta ostatnia notatka o potrzebie pełnego zestawu była dla mnie kluczowa. To był mój problem, dzięki!
Ben Trengrove
W 7sekcji: czy masz na myśli, że plik .so może zostać zmieniony z pliku APK po jego zainstalowaniu na urządzeniu? Jeśli tak, czy jest szansa, że ​​system może zniszczyć plik .so?
jayatubi
5
Wspaniale! W moim bardzo dziwnym przypadku korzystałem z zestawu bibliotek innej firmy (OpenCV - w folderze armeabi ) i te biblioteki przestały się ładować, gdy dodałem inną bibliotekę innej firmy za pośrednictwem Gradle. Okazuje się, że druga biblioteka nie obsługuje ARMv5 ani 6, a dołączając ją, moje biblioteki OpenCV stały się niewidoczne (chociaż faktycznie tam były ). Twoja uwaga dotycząca pełnego zestawu dała mi wskazówkę - zmiana nazwy folderu armeabi i nazwanie go armeabi-v7a rozwiązała problem (ponieważ teraz nie obsługuję ARM 5 ani 6 ...). Zły problem !!
Mete
1
Innym sposobem znalezienia miejsca, w którym należy umieścić plik lib (* .so), jest uruchomienie aplikacji i wydrukowanie katalogu nativeLibraryDir za pomocą: System.out.println (getApplicationContext (). GetApplicationInfo (). NativeLibraryDir), nazwa katalogu będzie również dostarczyć ci ABI.
David Rauca
20

W gradle, po skopiowaniu wszystkich plików folderów do libs/

jniLibs.srcDirs = ['libs']

Dodawanie powyższą linię sourceSetsw build.gradlepliku pracował. Nic innego nie działało.

Mukund Muralikrishnan
źródło
2
gdzie „sourceSets” znajduje się w pliku build.gradle?
Ashana.Jackol
12

Czy używasz gradle? Jeśli tak, włóż .soplik<project>/src/main/jniLibs/armeabi/

Mam nadzieję, że to pomoże.

Assaf Gamliel
źródło
nie, nie używam gradle, używam eclipse + maven
user842225
12

W moim przypadku muszę wykluczyć źródła kompilacji przez gradle i ustawić ścieżkę do bibliotek

android {

    ...
    sourceSets {
        ...
        main.jni.srcDirs = []
        main.jniLibs.srcDirs = ['libs']
    }
....
Dawid Drozd
źródło
to rozwiązało również dla mnie i dodałem pliki armeabi w folderach armeabi-v7a i x86, ale nie jestem pewien, czy to było konieczne.
dokam_scotland
8

Przyczyną tego błędu jest niezgodność ABI między aplikacją a biblioteką natywną, z którą utworzono łącze. Innymi słowy, Twoja aplikacja i Twoja .sosą ukierunkowane na różne ABI.

Jeśli tworzysz swoją aplikację przy użyciu najnowszych szablonów Android Studio, prawdopodobnie jest ona przeznaczona dla, arm64-v8aale Twoja .somoże być armeabi-v7ana przykład przeznaczona.

Istnieją 2 sposoby rozwiązania tego problemu:

  1. zbuduj natywne biblioteki dla każdego ABI, który obsługuje Twoja aplikacja.
  2. Zmień aplikację, aby była ukierunkowana na starsze ABI, dla których została .sostworzona.

Wybór 2 jest brudny, ale myślę, że prawdopodobnie bardziej interesują Cię:

zmień aplikację build.gradle

android {
    defaultConfig {
        ...
        ndk {
            abiFilters 'armeabi-v7a'
        }
   }
}
wdanxna
źródło
co jeśli moja aplikacja jest w zaćmieniu? Ten problem pojawia się, gdy przenoszę tę samą dostosowaną aplikację z 6 na 9.
Shadow
6

Dla porównania, otrzymałem ten komunikat o błędzie, a rozwiązaniem było to, że kiedy określasz bibliotekę, pomijasz „lib” z przodu i „.so” na końcu.

Więc jeśli masz plik libmyfablib.so, musisz zadzwonić:

   System.loadLibrary("myfablib"); // this loads the file 'libmyfablib.so' 

Po zajrzeniu do apk, zainstalowaniu / odinstalowaniu i wypróbowaniu wszelkiego rodzaju złożonych rozwiązań nie mogłem zobaczyć prostego problemu, który był tuż przed moją twarzą!

Andy Krouwel
źródło
To było to. Z jakiegoś nieznanego powodu Android nie instaluje bibliotek, których nazwa pliku nie zaczyna się od „lib”, nawet jeśli są obecne w pakiecie. Idź ...
George Y.
Gdzie mogę to sprawdzić w projekcie? Chodzi mi o to, gdzie mogę znaleźć tę linię System.loadLibraryw kodzie
aleksandrbel
Dzięki. To pomogło!
Riskhan
5

To jest aktualizacja Androida 8.

We wcześniejszej wersji Androida, do natywnych bibliotek współdzielonych LoadLibrary (na przykład w celu uzyskania dostępu przez JNI) na stałe połączyłem mój kod natywny, aby iterować przez szereg potencjalnych ścieżek katalogów dla folderu lib, w oparciu o różne algorytmy instalacji / aktualizacji apk:

/data/data/<PackageName>/lib
/data/app-lib/<PackageName>-1/lib
/data/app-lib/<PackageName>-2/lib
/data/app/<PackageName>-1/lib
/data/app/<PackageName>-2/lib

To podejście jest dziwaczne i nie będzie działać na Androidzie 8; ze strony https://developer.android.com/about/versions/oreo/android-8.0-changes.html zobaczysz, że w ramach ich zmian „Zabezpieczenia” musisz teraz użyć sourceDir:

„Nie można już zakładać, że pliki APK znajdują się w katalogach, których nazwy kończą się na -1 lub -2. Aplikacje powinny używać sourceDir do pobierania katalogu, a nie bezpośrednio polegać na formacie katalogu”.

Poprawka, sourceDir nie jest sposobem na znalezienie natywnych bibliotek współdzielonych; użyj czegoś takiego jak. Przetestowano pod kątem Androida 4.4.4 -> 8.0

// Return Full path to the directory where native JNI libraries are stored.
private static String getNativeLibraryDir(Context context) {
    ApplicationInfo appInfo = context.getApplicationInfo();
    return appInfo.nativeLibraryDir;
}
AlgebraWinter
źródło
4

Spróbuj zadzwonić do swojej biblioteki po uwzględnieniu PREBUILT_SHARED_LIBRARYsekcji:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := <PATH>/libcalculate.so
include $(PREBUILT_SHARED_LIBRARY)

#...

LOCAL_SHARED_LIBRARIES += libcalculate

Aktualizacja:

Jeśli będziesz używać tej biblioteki w Javie, musisz skompilować ją jako bibliotekę współdzieloną

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := <PATH>/libcalculate.so
include $(BUILD_SHARED_LIBRARY)

Musisz wdrożyć bibliotekę w /vendor/libkatalogu.

Alex
źródło
Dopiero na końcu sekcji.
Alex
2

Możesz po prostu zmienić ABI, aby używać starszych wersji:

defaultConfig {
    ...

    ndk {
        abiFilters 'armeabi-v7a'
    }
    ...
}

Powinieneś także użyć przestarzałego NDK, dodając ten wiersz do gradle.properties:

android.useDeprecatedNdk=true
rezam
źródło
0

dodaj wszystkie wsparcie

app / build.gradle

ndk {
        moduleName "serial_port"
        ldLibs "log", "z", "m"
        abiFilters "arm64-v8a","armeabi", "armeabi-v7a", "x86","x86_64","mips","mips64"
}

app \ src \ jni \ Application.mk

APP_ABI := arm64-v8a armeabi armeabi-v7a x86 x86_64 mips mips64
Lion 耿
źródło
1
Czy mógłbyś bardziej szczegółowo określić, co robić?
EFrank
Plik .so w swoim projekcie. Powinieneś wspierać arm64-v8a armeabi armeabi-v7a x86 x86_64 mips mips64. Kiedy to robiłem, działało dobrze.
Lion 耿
-1

Z mojego doświadczenia wynika, że ​​w telefonie komórkowym armeabi-v7a, gdy zarówno katalogi armeabi, jak i armeabi-v7a są obecne w apk, pliki .so w katalogu armeabi nie zostaną połączone, chociaż pliki .so w armeabi BĘDĄ połączone w ten sam armeabi-v7a mobile, jeśli armeabi-v7a nie jest obecny.

loopscn
źródło
-1

w rzeczywistości nie możesz po prostu umieścić pliku .so w pliku /libs/armeabi/i załadować go System.loadLibrary. Musisz utworzyć plik Android.mk i zadeklarować wstępnie skompilowany moduł, w którym określasz plik .so jako źródło.

Aby to zrobić, umieść plik .so i plik Android.mk w jnifolderze. Twój Android.mk powinien wyglądać mniej więcej tak:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := libcalculate
LOCAL_SRC_FILES := libcalculate.so
include $(PREBUILT_SHARED_LIBRARY)

Źródło: dokumentacja Android NDK dotycząca wstępnie skompilowanej wersji

Yannshu
źródło