Pojęcia

Zanim zaczniesz

W tym przewodniku zakładamy, że znasz już pojęcia charakterystyczne dla programowania natywnego i programowania Androida.

Wprowadzenie

Ta sekcja zawiera ogólne objaśnienie działania zestawu NDK. NDK na Androida to zestaw narzędzi umożliwiających umieszczanie C lub C++ („kod natywny”) w aplikacjach na Androida. Możliwość używania kodu natywnego w aplikacjach na Androida może być szczególnie przydatna dla deweloperów, którzy chcą wykonać co najmniej jedną z tych czynności:

  • przenosić aplikacje między platformami;
  • Możesz wykorzystać istniejące biblioteki lub udostępnić własne.
  • W niektórych przypadkach może to zwiększyć wydajność, zwłaszcza w przypadku gier wymagających dużej mocy obliczeniowej.

Jak to działa

W tej sekcji omawiamy główne komponenty wykorzystywane przy tworzeniu aplikacji natywnej na Androida, a w dalszej części omawiamy proces jej tworzenia i pakowania.

Główne komponenty

Tworząc aplikację, musisz znać te komponenty:

  • Natywne biblioteki udostępnione: NDK skompiluje te biblioteki (pliki .so) z kodu źródłowego w C/C++.

  • Natywne biblioteki statyczne: pakiet NDK może też tworzyć biblioteki statyczne (czyli pliki .a), które możesz połączyć z innymi bibliotekami.

  • Java Native Interface (JNI): JNI to interfejs, za pomocą którego komponenty Java i C++ komunikują się ze sobą. W tym przewodniku zakładamy, że zna się na JNI. Więcej informacji na ten temat można znaleźć w specyfikacji interfejsu Java Native Interface.

  • Interfejs binarny aplikacji (ABI): interfejs ABI dokładnie definiuje sposób interakcji kodu komputera aplikacji z systemem w czasie działania. NDK skompiluje pliki .so na podstawie tych definicji. Różne interfejsy ABI odpowiadają różnym architekturze. NDK obejmuje obsługę ABI dla 32-bitowych systemów ARM, AArch64, x86 i x86-64. Więcej informacji znajdziesz w artykule o ABI na Androida.

  • Plik manifestu: jeśli tworzysz aplikację bez komponentu Java, musisz zadeklarować klasę NativeActivity w pliku manifestu. Więcej informacji o tym, jak to zrobić, znajdziesz w sekcji dotyczącej interfejsunative_activity.h.

Przepływ

Ogólna procedura tworzenia aplikacji natywnej na Androida wygląda tak:

  1. Zaprojektuj aplikację, wybierając fragmenty do zaimplementowania w języku Java, a które jako kod natywny.

  2. Utwórz projekt aplikacji na Androida tak samo jak w przypadku każdego innego projektu na Androida.

  3. Jeśli tworzysz aplikację tylko natywną, zadeklaruj klasę NativeActivity w AndroidManifest.xml. Więcej informacji znajdziesz w artykule o natywnych działaniach i aplikacjach.

  4. Utwórz plik Android.mk opisujący bibliotekę natywną, w tym jej nazwę, flagi, połączone biblioteki i pliki źródłowe, które mają zostać skompilowane w katalogu „JNI”.

  5. Opcjonalnie możesz utworzyć plik Application.mk zawierający ustawienia docelowego środowiska AI, łańcucha narzędzi, trybu wersji/debugowania oraz STL. W przypadku elementów, których nie określisz, używane są odpowiednio te wartości domyślne:

    • ABI: wszystkie niewycofane interfejsy ABI
    • Tryb: zwolnienie
    • STL: system
  6. Umieść swoje natywne źródło w katalogu jni projektu.

  7. Użyj narzędzia ndk-build, aby skompilować biblioteki natywne (.so, .a).

  8. Skompiluj komponent Javy, tworząc wykonywalny plik .dex.

  9. Spakuj wszystko do pliku APK, który zawiera .so, .dex i inne pliki niezbędne do działania aplikacji.

Aktywności i aplikacje natywne

Pakiet SDK do Androida udostępnia klasę pomocniczą NativeActivity, która umożliwia pisanie całkowicie natywnej aktywności. klasa NativeActivity obsługuje komunikację między platformą Androida a kodem natywnym, więc nie musisz jej podklasyfikować ani wywoływać jej metod. Musisz tylko zadeklarować w pliku AndroidManifest.xml, że aplikacja jest natywna i zacząć tworzyć aplikację natywną.

Aplikacja na Androida korzystająca z metody NativeActivity nadal działa we własnej maszynie wirtualnej w piaskownicy od innych aplikacji. W związku z tym nadal możesz uzyskać dostęp do interfejsów API platformy Android za pomocą interfejsu JNI. W niektórych przypadkach, na przykład w przypadku czujników, zdarzeń wejściowych i zasobów, NDK zapewnia interfejsy natywne, których można używać zamiast wywoływać przez JNI. Więcej informacji o takiej obsłudze znajdziesz w artykule o natywnych interfejsach API.

Niezależnie od tego, czy tworzysz działania natywne, zalecamy tworzenie projektów za pomocą tradycyjnych narzędzi do tworzenia aplikacji na Androida. Pomaga to w tworzeniu i pakowaniu aplikacji na Androida o prawidłowej strukturze.

Pakiet Android NDK oferuje 2 możliwości wdrożenia aktywności natywnej:

  • Nagłówek native_activity.h określa natywną wersję klasy NativeActivity. Zawiera on interfejs wywołania zwrotnego i struktury danych potrzebne do utworzenia aktywności natywnej. Ponieważ wątek główny aplikacji obsługuje wywołania zwrotne, implementacje wywołań zwrotnych nie mogą być blokowane. Jeśli zostaną zablokowane, mogą wystąpić błędy ANR (Aplikacja nie odpowiada), ponieważ wątek główny nie odpowiada, dopóki nie zostanie zwrócone wywołanie zwrotne.
  • Plik android_native_app_glue.h definiuje statyczną bibliotekę pomocniczą utworzoną na interfejsie native_activity.h. Pojawia się kolejny wątek, który obsługuje takie elementy jak wywołania zwrotne i zdarzenia wejściowe w pętli zdarzeń. Przeniesienie tych zdarzeń do osobnego wątku zapobiega blokowaniu przez wywołania zwrotne Twojego wątku głównego.

Dostępne jest też źródło <ndk_root>/sources/android/native_app_glue/android_native_app_glue.c, które umożliwia modyfikowanie implementacji.

Aby dowiedzieć się więcej o korzystaniu z tej biblioteki statycznej, zapoznaj się z przykładową aplikacją natywnej aktywności i jej dokumentacją. Informacje na ten temat można też znaleźć w komentarzach w pliku <ndk_root>/sources/android/native_app_glue/android_native_app_glue.h.

Korzystanie z interfejsu natywnego_activity.h

Aby zaimplementować aktywność natywną za pomocą interfejsu native_activity.h:

  1. Utwórz katalog jni/ w katalogu głównym projektu. W tym katalogu przechowywany jest cały kod natywny.

  2. Zadeklaruj aktywność natywną w pliku AndroidManifest.xml.

    Twoja aplikacja nie zawiera kodu Java, więc ustaw android:hasCode na false.

    <application android:label="@string/app_name" android:hasCode="false">
    

    Musisz ustawić atrybut android:name tagu aktywności na NativeActivity.

    <activity android:name="android.app.NativeActivity"
              android:label="@string/app_name">
    

    Atrybut android:value tagu meta-data określa nazwę biblioteki współdzielonej zawierającej punkt wejścia do aplikacji (np. C/C++ main), z pominięciem prefiksu lib i sufiksu .so w nazwie biblioteki.

    <manifest>
      <application>
        <activity>
          <meta-data android:name="android.app.lib_name"
                     android:value="native-activity" />
          <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
        </activity>
      </application>
    </manifest>
    
  3. Utwórz plik dla aktywności natywnej i zaimplementuj funkcję o nazwie we zmiennej ANativeActivity_onCreate. Aplikacja wywołuje tę funkcję, gdy uruchamia się aktywność natywna. Ta funkcja, podobnie jak main w C/C++, otrzymuje wskaźnik do struktury ANativeActivity, która zawiera wskaźniki funkcji do różnych implementacji wywołania zwrotnego, które musisz zapisać. Ustaw odpowiednie wskaźniki funkcji wywołania zwrotnego w ANativeActivity->callbacks na implementacje swoich wywołań zwrotnych.

  4. W polu ANativeActivity->instance wpisz adres dowolnej instancji danych, której chcesz używać.

  5. Na początek zastosuj w działaniu wszystkie inne czynności, które chcesz realizować.

  6. Zaimplementuj pozostałe wywołania zwrotne ustawione w sekcji ANativeActivity->callbacks. Więcej informacji o tym, kiedy są wywoływane wywołania zwrotne, znajdziesz w artykule Zarządzanie cyklem życia aktywności.

  7. Zaprojektuj pozostałą część aplikacji.

  8. Utwórz obiekt Android.mk file w katalogu jni/ projektu, aby opisać moduł natywny w systemie kompilacji. Więcej informacji znajdziesz w witrynie Android.mk.

  9. Gdy uzyskasz plik Android.mk, skompiluj kod natywny przy użyciu polecenia ndk-build.

    cd <path>/<to>/<project>
    $NDK/ndk-build
    
  10. Utwórz i zainstaluj projekt na Androida w zwykły sposób. Jeśli Twój kod natywny znajduje się w katalogu jni/, skrypt kompilacji automatycznie spakuje wbudowane z niego pliki .so do pliku APK.

Dodatkowy przykładowy kod

Próbki NDK można pobrać na stronie Próbki NDK.