Próbka aktywności natywnej znajduje się w sekcji
NDK samples root, w folderze
native-activity
To bardzo prosty przykład reklamy rodzimych
bez kodu źródłowego w Javie. Jeśli nie ma żadnego źródła w Javie,
Kompilator Java nadal tworzy wykonywalny kod pośredni, aby uruchomić maszynę wirtualną.
Ten kod pełni funkcję kodu rzeczywistego programu natywnego, który znajduje się w regionie .so
.
Aplikacja po prostu renderuje kolor na całym ekranie, a potem zmienia kolor częściowo w odpowiedzi na wykryte ruchy.
AndroidManifest.xml
Aplikacja zawierająca tylko kod natywny nie może określać poziomu interfejsu API Androida niższy niż 9, co spowodowało wprowadzenie
klasy platformy NativeActivity
.
<uses-sdk android:minSdkVersion="9" />
W tym wierszu zadeklarowano android:hasCode
jako false
, ponieważ ta aplikacja ma tylko
kodu natywnego – bez Javy.
<application android:label="@string/app_name"
android:hasCode="false">
W następnym wierszu deklaruje się klasę NativeActivity
.
<activity android:name="android.app.NativeActivity"
Natomiast w pliku manifestu wskazuje android:value
jako nazwę biblioteki współdzielonej, która ma zostać
utworzone, minus początkowe lib
i rozszerzenie .so
. Ta wartość musi być taka sama jak
imię i nazwisko użytkownika LOCAL_MODULE
w języku: Android.mk
.
<meta-data android:name="android.app.lib_name" android:value="native-activity" />
Android.mk
Ten plik zaczyna się od nazwy biblioteki udostępnionej, która ma zostać wygenerowana.
LOCAL_MODULE := native-activity
Następnie deklaruje nazwę natywnego pliku z kodem źródłowym.
LOCAL_SRC_FILES := main.c
Następnie zawiera listę bibliotek zewnętrznych, które system kompilacji ma wykorzystać przy tworzeniu pliku binarnego.
-l
(link do) poprzedza każdą nazwę biblioteki.
log
to biblioteka logów.android
obejmuje standardowe interfejsy API do obsługi Androida dla NDK. Więcej informacji na temat: Interfejsy API obsługiwane przez Androida i NDK znajdziesz w sekcji Android NDK Native na Androida interfejsów API.EGL
odpowiada specyficznej dla platformy części interfejsu API grafiki.GLESv1_CM
odpowiada OpenGL ES, wersji OpenGL dla Androida. Ta biblioteka zależy od EGL.
Dla każdej biblioteki:
- Rzeczywista nazwa pliku rozpoczyna się od
lib
i kończy.so
. Na przykład: rzeczywista nazwa pliku Bibliotekalog
jestliblog.so
. - Znajdziesz ją w katalogu głównym NDK:
<ndk>/platforms/android-<sdk_version>/arch-<abi>/usr/lib/
LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM
W następnym wierszu znajduje się nazwa biblioteki statycznej android_native_app_glue
, której
której aplikacja używa do zarządzania zdarzeniami cyklu życia NativeActivity
i dotykowym wprowadzaniem danych.
LOCAL_STATIC_LIBRARIES := android_native_app_glue
Ostatni wiersz informuje system kompilacji, że ma utworzyć tę bibliotekę statyczną.
Skrypt ndk-build
umieszcza utworzoną bibliotekę
(libandroid_native_app_glue.a
) do katalogu obj
generowanych podczas procesu kompilacji. Aby uzyskać więcej informacji na temat: android_native_app_glue
, zobaczysz jej nagłówek android_native_app_glue.h
i odpowiadający mu .c
plik źródłowy.
$(call import-module,android/native_app_glue)
Więcej informacji o pliku Android.mk
znajdziesz tutaj:
Android.mk
main.c
Ten plik zawiera zasadniczo cały program.
Następujące biblioteki odpowiadają bibliotekom, zarówno udostępnionym, jak i statycznym,
omówione w artykule Android.mk
.
#include <EGL/egl.h> #include <GLES/gl.h> #include <android/sensor.h> #include <android/log.h> #include <android_native_app_glue>
Biblioteka android_native_app_glue
wywołuje następującą funkcję:
przekazywanie jej do wstępnie zdefiniowanej struktury stanu. Służy również jako kod
upraszcza obsługę wywołań zwrotnych NativeActivity
.
void android_main(struct android_app* state) {
Następnie program obsługuje zdarzenia umieszczone w kolejce przez bibliotekę glue. Wydarzenie zgodnie ze strukturą stanową.
struct engine engine; // Suppress link-time optimization that removes unreferenced code // to make sure glue isn't stripped. app_dummy(); memset(&engine, 0, sizeof(engine)); state->userData = &engine; state->onAppCmd = engine_handle_cmd; state->onInputEvent = engine_handle_input; engine.app = state;
Aplikacja przygotowuje się do rozpoczęcia monitorowania czujników, wykorzystując
Interfejsy API w regionie sensor.h
.
engine.sensorManager = ASensorManager_getInstance(); engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, ASENSOR_TYPE_ACCELEROMETER); engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager, state->looper, LOOPER_ID_USER, NULL, NULL);
Rozpoczyna się pętla, w której aplikacja wysyła do systemu
wiadomości (zdarzenia czujnika). Wysyła wiadomości do
android_native_app_glue
, który sprawdza, czy pasują
wszystkie zdarzenia onAppCmd
zdefiniowane w android_main
. Gdy
wiadomość zostaje wysłana do modułu obsługi w celu wykonania.
while (1) { // Read all pending events. int ident; int events; struct android_poll_source* source; // If not animating, we will block forever waiting for events. // If animating, we loop until all events are read, then continue // to draw the next frame of animation. while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events, (void**)&source)) >= 0) { // Process this event. if (source != NULL) { source->process(state, source); } // If a sensor has data, process it now. if (ident == LOOPER_ID_USER) { if (engine.accelerometerSensor != NULL) { ASensorEvent event; while (ASensorEventQueue_getEvents(engine.sensorEventQueue, &event, 1) > 0) { LOGI("accelerometer: x=%f y=%f z=%f", event.acceleration.x, event.acceleration.y, event.acceleration.z); } } } // Check if we are exiting. if (state->destroyRequested != 0) { engine_term_display(&engine); return; } }
Gdy kolejka jest pusta, a program zamyka pętlę odpytywania, program wywołuje funkcję OpenGL w celu rysowania ekranu.
if (engine.animating) { // Done with events; draw next animation frame. engine.state.angle += .01f; if (engine.state.angle > 1) { engine.state.angle = 0; } // Drawing is throttled to the screen update rate, so there // is no need to do timing here. engine_draw_frame(&engine); } }