Typowe problemy i ich rozwiązania

Ten dokument zawiera częściową listę najczęstszych błędów, które mogą wystąpić podczas korzystania z pakietu NDK, wraz z ich rozwiązaniami (jeśli są dostępne).

Używanie _FILE_OFFSET_BITS=64 ze starszymi poziomami API

Przed ujednoliconymi nagłówkami pakiet NDK nie obsługiwał _FILE_OFFSET_BITS=64. Jeśli określisz ją podczas tworzenia aplikacji, zostanie ona dyskretnie zignorowana. Opcja _FILE_OFFSET_BITS=64 jest teraz obsługiwana z ujednoliconymi nagłówkami, ale w starszych wersjach Androida bardzo niewiele interfejsów API off_t było dostępnych jako wariant off64_t. Dlatego użycie tej funkcji na starszych poziomach API zmniejsza liczbę dostępnych funkcji.

Szczegółowo wyjaśniliśmy ten problem w poście na blogu R16 i w dokumentacji systemu Bionic.

Problem: kompilacja prosi o interfejsy API, których nie ma w minSdkVersion.

Rozwiązanie: wyłącz _FILE_OFFSET_BITS=64 lub zwiększ minSdkVersion.

Niezadeklarowana lub niejawna definicja słowa mmap

W C++ może pojawić się taki błąd:

błąd: użycie niezadeklarowanego identyfikatora „mmap”

lub następujący błąd w C:

ostrzeżenie: niejawna deklaracja funkcji „mmap” jest nieprawidłowa w C99

Użycie właściwości _FILE_OFFSET_BITS=64 powoduje, że biblioteka C używa właściwości mmap64 zamiast mmap. Usługa mmap64 była niedostępna do android-21. Jeśli wartość minSdkVersion jest mniejsza niż 21, biblioteka C nie zawiera elementu mmap zgodnej z _FILE_OFFSET_BITS=64, więc funkcja jest niedostępna.

Element minSdkVersion jest ustawiony wyżej niż poziom interfejsu API urządzenia

Poziom interfejsu API, do którego odnosi się pakiet NDK, ma zupełnie inne znaczenie niż compileSdkVersion w Javie. Poziom interfejsu NDK API to minimalny obsługiwany poziom interfejsu API aplikacji. W ndk-build to jest Twoje ustawienie APP_PLATFORM. W CMake jest to -DANDROID_PLATFORM.

Odniesienia do funkcji są zwykle rozpoznawane podczas wczytywania bibliotek, a nie przy ich pierwszym wywołaniu, dlatego nie możesz odwoływać się do interfejsów API, które nie zawsze są dostępne, i kontrolować ich użycie za pomocą kontroli na poziomie interfejsu API. Jeśli w ogóle się do nich odwołujesz, muszą być obecni.

Problem: poziom interfejsu NDK API jest wyższy niż interfejs API obsługiwany przez Twoje urządzenie.

Rozwiązanie: ustaw poziom interfejsu NDK API (APP_PLATFORM) na minimalną wersję Androida obsługiwaną przez Twoją aplikację.

System kompilacji Ustawienie
ndk-build APP_PLATFORM
Tworzenie środowiska wykonawczego ANDROID_PLATFORM
externalNativeBuild android.minSdkVersion

W przypadku innych systemów kompilacji przeczytaj artykuł o używaniu pakietu NDK z innymi systemami kompilacji.

Nie można znaleźć symboli __aeabi

Następująca wiadomość:

UnsatisfiedLinkError: dlopen Błąd: nie można znaleźć symbolu „__aeabi_memcpy

to jeden z przykładów możliwych błędów w środowisku wykonawczym. Te błędy pojawiają się w dzienniku, gdy próbujesz wczytać swoje biblioteki natywne. Może to być dowolny z tych symboli: __aeabi_*. __aeabi_memcpy i __aeabi_memclr są najpopularniejsze.

Ten problem jest opisany w numerze 126

Nie można znaleźć symbolu rand

Dla tego komunikatu w dzienniku błędów:

UnsatisfiedLinkError: dlopen Błąd: nie można znaleźć symbolu „rand

Zapoznaj się ze szczegółową odpowiedzią w usłudze Stack Overflow.

Niezdefiniowane odwołanie do: __atomic_*

Problem: niektóre interfejsy ABI wymagają libatomic do udostępnienia implementacji niepodzielnych operacji.

Rozwiązanie: podczas łączenia dodaj atrybut -latomic.

W przypadku tego komunikatu o błędzie:

błąd: niezdefiniowane odniesienie do atrybutu „__atomic_exchange_4

faktycznym symbolem w tym miejscu może być dowolny element z prefiksem __atomic_.

RTTI/wyjątki nie działają poza granicami bibliotek

Problem: wyjątki nie są wykrywane w przypadku zgłaszania ich poza granicami biblioteki udostępnionej lub usługa dynamic_cast nie działa.

Rozwiązanie: dodaj do typów funkcję kluczową. Funkcja kluczowa to pierwsza nieczysta, pozawierszowa funkcja wirtualna danego typu. Oto przykład w dyskusji na temat numeru 533.

Interfejs C++ ABI stwierdza, że 2 obiekty są tego samego typu tylko wtedy, gdy ich wskaźniki type_info są identyczne. Wyjątki mogą być przechwytywane tylko wtedy, gdy element type_info dla webhooka jest zgodny z zgłoszonym wyjątkiem. Ta sama reguła dotyczy dynamic_cast.

Gdy typ nie ma funkcji klucza, jego typeinfo jest emitowany jako słaby symbol, a informacje o typie dopasowania są scalane podczas wczytywania bibliotek. W przypadku dynamicznego wczytywania bibliotek po załadowaniu pliku wykonywalnego (inaczej: za pomocą dlopen lub System.loadLibrary), moduł ładowania może nie być w stanie scalić informacji o typach załadowanych bibliotek. W takiej sytuacji ich 2 typy nie są sobie równe.

Używanie niedopasowanych gotowych bibliotek

Korzystanie w aplikacji z gotowych bibliotek (zwykle jest to biblioteki innych firm) wymaga dodatkowej uwagi. Ogólnie pamiętaj o tych regułach:

  • Wynikowy minimalny poziom interfejsu API aplikacji to maksymalna liczba korzystających z funkcji minSdkVersion wszystkich bibliotek aplikacji.

    Jeśli minSdkVersion ma wartość 16, ale używasz gotowej biblioteki skompilowanej pod kątem wersji 21, minimalny poziom interfejsu API aplikacji to 21. Jeśli tego nie zrobisz, będzie to widoczne podczas kompilacji, jeśli gotowa biblioteka jest statyczna, ale może się pojawić dopiero w czasie wykonywania gotowych bibliotek udostępnionych.

  • Wszystkie biblioteki powinny być generowane w tej samej wersji pakietu NDK.

    Ta reguła jest trochę bardziej elastyczna niż większość większości, ponieważ awarie występują rzadko, ale zgodność między bibliotekami, które zostały utworzone z różnymi głównymi wersjami pakietu NDK, nie jest gwarantowana. Ten interfejs ABI C++ nie jest stabilny i w przeszłości zmienił się.

  • Aplikacje z wieloma bibliotekami udostępnionymi muszą używać udostępnionego STL.

    Tak jak w przypadku niedopasowanych obrazów STL, problemów wynikających z tego problemu można uniknąć, dbając o odpowiednią ostrożność, ale lepiej po prostu unikać takich problemów. Najlepszym sposobem uniknięcia tego problemu jest unikanie posiadania w aplikacji wielu bibliotek udostępnionych.