Rozpowszechnianie oprogramowania pośredniczącego utworzonego w ramach pakietu NDK wiąże się z kilkoma dodatkowymi problemami, którymi deweloperzy aplikacji nie muszą się martwić. Gotowe biblioteki narzucają użytkownikom niektóre opcje implementacji.
Wybieranie poziomów interfejsu API i wersji NDK
Twoi użytkownicy nie mogą używać wartości minSdkVersion niższej niż Twoja. Jeśli aplikacje Twoich użytkowników muszą korzystać z interfejsu API 21, nie możesz tworzyć kompilacji pod kątem tego interfejsu. Możesz tworzyć biblioteki na niższym poziomie interfejsu API niż użytkownicy. Możesz tworzyć kompilacje dla interfejsu API 16 i zachować zgodność z użytkownikami interfejsu API 21.
Wersje NDK są w większości zgodne ze sobą, ale czasami zdarzają się zmiany, które zakłócają zgodność. Jeśli wiesz, że wszyscy użytkownicy korzystają z tej samej wersji pakietu NDK, najlepiej jest używać tej samej wersji, co oni. W przeciwnym razie używaj najnowszej wersji.
Używanie STL
Jeśli piszesz w języku C++ i używasz STL, wybór między libc++_shared
a libc++_static
będzie miał wpływ na Twoich użytkowników, jeśli rozpowszechniasz zasoby udostępniane. Jeśli rozpowszechniasz zasoby udostępniane, musisz użyć zasady libc++_shared
lub upewnić się, że symbole z biblioteki libc++ nie są widoczne w bibliotece. Najlepiej zadeklarować platformę ABI za pomocą skryptu wersji (pomoże to także zachować prywatność szczegółów implementacji). Na przykład prosta biblioteka arytmetyczna może mieć taki skrypt wersji:
LIBMYMATH {
global:
add;
sub;
mul;
div;
# C++ symbols in an extern block will be mangled automatically. See
# https://stackoverflow.com/a/21845178/632035 for more examples.
extern "C++" {
"pow(int, int)";
}
local:
*;
};
Skrypt wersji powinien być opcją preferowaną, ponieważ jest to najpewniejszy sposób kontrolowania widoczności symboli. Jest to sprawdzona metoda w przypadku wszystkich bibliotek wspólnych oraz oprogramowania pośredniczącego, ponieważ zapobiega ujawnieniu szczegółów implementacji i skraca czas wczytywania.
Inną, mniej niezawodną opcją, jest użycie elementu -Wl,--exclude-libs,libc++_static.a
-Wl,--exclude-libs,libc++abi.a
do łączenia. Jest to mniej niezawodne, ponieważ ukrywa tylko symbole w bibliotekach, które mają jawne nazwy, a w przypadku nieużywanych bibliotek nie są zgłaszane żadne dane diagnostyczne (literówka w nazwie biblioteki nie jest błędem, a koniecznością aktualizowania listy bibliotek jest użytkownik). W ten sposób nie ukryjesz też szczegółów Twojej implementacji.
Rozpowszechnianie bibliotek natywnych w AAR
Wtyczka Androida do obsługi Gradle może importować zależności natywne rozpowszechniane w plikach AAR. Jeśli Twoi użytkownicy używają wtyczki Androida do obsługi Gradle, jest to najprostszy sposób na korzystanie z Twojej biblioteki.
Biblioteki natywne można spakować do AAR w AGP. Jest to najprostsza opcja, jeśli biblioteka została już skompilowana za pomocą externalNativeBuild.
Kompilacje w środowisku innym niż AGP mogą korzystać z ndkports lub ręcznie tworzyć pakowanie, postępując zgodnie z dokumentacją Prefab i tworząc podkatalog prefab/
pliku AAR.
Oprogramowanie pośredniczące w Javie z bibliotekami JNI
Biblioteki Java zawierające biblioteki JNI (czyli biblioteki AAR zawierające jniLibs
) muszą zadbać o to, aby zawarte w nich biblioteki JNI nie kolidowały z innymi bibliotekami w aplikacji użytkownika. Jeśli na przykład AAR zawiera libc++_shared.so
, ale inna wersja libc++_shared.so
niż używana przez aplikację, w pakiecie APK zostanie zainstalowana tylko jedna, co może prowadzić do niestabilnego działania.
Najbardziej niezawodnym rozwiązaniem jest dodanie do bibliotek Javy nie więcej niż jednej biblioteki JNI (to też dobra rada w przypadku aplikacji). Wszystkie zależności, w tym STL, powinny być statycznie połączone z biblioteką implementacji, a do wymuszania platformy ABI należy używać skryptu wersji. Na przykład biblioteka Java com.example.foo
, która zawiera bibliotekę JNI libfooimpl.so
, powinna używać tego skryptu:
LIBFOOIMPL {
global:
JNI_OnLoad;
local:
*;
};
W tym przykładzie użyto protokołu registerNatives
za pomocą JNI_OnLoad
, zgodnie ze wskazówkami dotyczącymi JNI, aby odsłonić minimalną powierzchnię interfejsu ABI i skrócić czas wczytywania biblioteki.