2013-07-16 10 views
17

Zmarnowane przez pół dnia próby zbudowania dwóch udostępnionych bibliotek, np. mod1 i mod2 (które Android NDK kompiluje do libmod1.so i libmod2.so), ze źródeł w folderze jni i podfolderach, a następnie mają mod1 wywoływanie funkcji z mod2. Mnóstwo odpowiedzi na pytanie, jak sprawić, aby kompilacja działała, ale wtedy dynamiczne łączenie w czasie wykonywania nie działało, aplikacja uległa awarii podczas uruchamiania.Android NDK - tworzenie dwóch natywnych bibliotek współdzielonych, które wywołują się nawzajem

Postanowiła opublikować to pytanie i natychmiast odpowiedzieć na nie, tak aby Q i A do całego procesu były razem, i mam nadzieję, że ktoś inny nie zmarnuje ani jednego dnia.

Odpowiedz

22

Poprawna procedura kompilacji była stosunkowo łatwa, moim problemem było to, że uzależnienie libmod1.so od libmod2.so powodowało niezadowalające linki przy starcie - kod mod1 nie mógł znaleźć biblioteki współdzielonej mod2, mimo że oba były obecne w tym samym folderze w ostateczna APK, pod libs/armeabi, libs/x86 itd. Jednakże, aby moja odpowiedź pełna:

  • Połóż C lub C++ źródła i pliki nagłówkowe pod podkatalogów z JNI dir w projekcie z Androidem na przykład foldery mod1/i mod2/

  • Według instrukcji NDK, utwórz plik Application.mk, np. Kopalnia jest:

NDK_TOOLCHAIN_VERSION = 4,7
APP_PLATFORM: = android-8
APP_ABI: = armeabi armeabi-v7a x86

  • Tworzenie Android.mk następstwie tego szablonu:

LOCAL_PATH: = $ (wezwanie my-dir)
obejmują $ (CLEAR_VARS)
LOCAL_SHARED_LIBRARIES: = MOD2       # To sprawia libmod1.so zależne libmod2.so
LOCAL_MODULE: = mod1
LOCAL_SRC_FILES: = mod1 /file1.c
LOCAL_SRC_FILES + = mod1/file2.cpp
...
obejmują $ (BUILD_SHARED_LIBRARY)       # to faktycznie buduje libmod1.so

obejmują $ (CLEAR_VARS)
LOCAL_MODULE: = MOD2
LOCAL_SRC_FILES: = MOD2/file1.cc
LOCAL_SRC_FILES + = MOD2/file2.cc
...
obejmują $ (BUILD_SHARED_LIBRARY)       # To buduje libmod2 .so

To wszystko, wszystkie kompilacje bez skarg ze skryptem ndkbuild. Do wywoływania niektórych funkcji z Javy potrzebujesz tylko opakowania C. I oto był mój problem. Ponieważ miałem funkcje wywoływalne z Java tylko w libmod1.tak, mój C klasy otoki w Javie było jak:

public class CWrapper { 
    static { 
     System.loadLibrary("mod1"); 
    } 
    public static native int func1(String aParam); 
    ... 
} 

Wydawało mi się zupełnie logiczne - Zadzwonię do libmod1.so z Jawy, więc użyłem System.loadLibrary („Mod1”), a od libmod1. więc wie, że to zależy od libmod2.so, a oba pliki są w tym samym folderze, libmod1 będzie wiedział, jak znaleźć i załadować libmod2, prawda? Źle! Po uruchomieniu aplikacji nastąpił awaria z "niezadowolonym łączem". Dokładny komunikat o błędzie:

java.lang.UnsatisfiedLinkError: Cannot load library: soinfo_link_image(linker.cpp:1635): could not load library "libmod2.so" needed by "libmod1.so"; caused by load_library(linker.cpp:745): library "libmod2.so" not found 

Szukałem wszędzie na trochę więcej kodu, aby dodać do Android.mk aby rozwiązać ten problem na marne. Wreszcie Eureka! I zmodyfikowane moją klasę CWrapper następująco:

public class CWrapper { 
    static { 
     System.loadLibrary("mod2"); // must be first, as mod1 depends on mod2! 
     System.loadLibrary("mod1"); 
    } 
    public static native int func1(String aParam); 
    ... 
} 

i rzeczy rozpoczął pracę jak czar ...

Greg

+1

Z tego możemy wywnioskować, że dynamiczne sprawdza system jest ładowarka i obciążenia (standardowe Zależności zachowanie), podczas gdy program ładujący Java nie. – Samveen

+0

To jest dobra obserwacja, @Samveen. Może powinienem przetestować to ponownie w nowszych wersjach Androida, aby sprawdzić, czy ostatnio coś ulepszyły. – gregko