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"
źródło
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 trafialibs/armeabi/
, itp).unzip -l package.apk
Lub 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?).Odpowiedzi:
Aby usunąć przyczynę (i być może w tym samym czasie, rozwiązać problem), oto co możesz zrobić:
Usuń folder jni i wszystkie pliki .mk . Nie potrzebujesz ich ani NDK, jeśli niczego nie kompilujesz.
Skopiuj
libcalculate.so
plik 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.Zbuduj swój plik APK i otwórz go jako plik zip , aby sprawdzić, czy
libcalculate.so
plik znajduje się w bibliotece lib / (armeabi | armeabi-v7a | x86 | ...) .Usuń i zainstaluj aplikację
Uruchom pakiety pakietów dumpsys | grep yourpackagename, aby uzyskać nativeLibraryPath lub legacyNativeLibraryDir swojej aplikacji.
Uruchom ls na nativeLibraryPath , który miałeś, lub na legacyNativeLibraryDir / armeabi , aby sprawdzić, czy plik libcalculate.so rzeczywiście tam jest.
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.
źródło
7
sekcji: 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?W gradle, po skopiowaniu wszystkich plików folderów do
libs/
jniLibs.srcDirs = ['libs']
Dodawanie powyższą linię
sourceSets
wbuild.gradle
pliku pracował. Nic innego nie działało.źródło
Czy używasz gradle? Jeśli tak, włóż
.so
plik<project>/src/main/jniLibs/armeabi/
Mam nadzieję, że to pomoże.
źródło
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'] } ....
źródło
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
.so
są ukierunkowane na różne ABI.Jeśli tworzysz swoją aplikację przy użyciu najnowszych szablonów Android Studio, prawdopodobnie jest ona przeznaczona dla,
arm64-v8a
ale Twoja.so
może byćarmeabi-v7a
na przykład przeznaczona.Istnieją 2 sposoby rozwiązania tego problemu:
.so
stworzona.Wybór 2 jest brudny, ale myślę, że prawdopodobnie bardziej interesują Cię:
zmień aplikację
build.gradle
android { defaultConfig { ... ndk { abiFilters 'armeabi-v7a' } } }
źródło
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ą!
źródło
System.loadLibrary
w kodzieTo 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; }
źródło
Spróbuj zadzwonić do swojej biblioteki po uwzględnieniu
PREBUILT_SHARED_LIBRARY
sekcji: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/lib
katalogu.źródło
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
źródło
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
źródło
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.
źródło
w rzeczywistości nie możesz po prostu umieścić pliku .so w pliku
/libs/armeabi/
i załadować goSystem.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
jni
folderze. 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
źródło