Przykład: aktywność natywna

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 Biblioteka log jest liblog.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 .cplik ź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);
    }
}