Esempio: attività nativa

L'esempio di attività nativa si trova nel Root di esempio di NDK, nella cartella native-activity. È un esempio molto semplice di un'architettura senza codice sorgente Java. In assenza di origini Java, Il compilatore Java crea comunque uno stub eseguibile per l'esecuzione della macchina virtuale. Lo stub funge da wrapper per il programma nativo effettivo, che si trova nell'elemento .so .

L'app stessa visualizza semplicemente un colore sull'intero schermo cambia parzialmente il colore in risposta ai movimenti rilevati.

AndroidManifest.xml

Per un'app con solo codice nativo non deve specificare un livello API Android inferiore a 9, che ha introdotto la classe framework NativeActivity.

<uses-sdk android:minSdkVersion="9" />

La riga seguente dichiara android:hasCode come false, poiché questa app ha codice nativo, non Java.

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

La riga successiva dichiara la classe NativeActivity.

<activity android:name="android.app.NativeActivity"

Infine, il file manifest specifica android:value come nome della libreria condivisa da utilizzare creata, meno la lib iniziale e l'estensione .so. Questo valore deve essere uguale a il nome di LOCAL_MODULE in Android.mk.

<meta-data android:name="android.app.lib_name"
        android:value="native-activity" />

Android.MK

Per iniziare, questo file indica il nome della libreria condivisa da generare.

LOCAL_MODULE    := native-activity

Successivamente, dichiara il nome del file di codice sorgente nativo.

LOCAL_SRC_FILES := main.c

Quindi, elenca le librerie esterne per il sistema di compilazione da utilizzare nella creazione del file binario. La L'opzione -l (link contro) precede ogni nome della raccolta.

  • log è una libreria di logging.
  • android comprende le API di supporto Android standard per NDK. Per ulteriori informazioni sulle API supportate da Android e NDK; consulta l'articolo relativo alla tecnologia Android NDK Native per le API.
  • EGL corrisponde alla parte dell'API grafica specifica per la piattaforma.
  • GLESv1_CM corrisponde a OpenGL ES, la versione di OpenGL per Android. Questa libreria dipende da EGL.

Per ogni libreria:

  • Il nome effettivo del file inizia con lib e termina con .so. Ad esempio, il nome effettivo del file La raccolta log è liblog.so.
  • La libreria si trova nella seguente directory, NDK root: <ndk>/platforms/android-<sdk_version>/arch-<abi>/usr/lib/.
LOCAL_LDLIBS    := -llog -landroid -lEGL -lGLESv1_CM

Nella riga successiva viene specificato il nome della libreria statica, android_native_app_glue, di cui utilizzata per gestire NativeActivity eventi del ciclo di vita e input tocco.

LOCAL_STATIC_LIBRARIES := android_native_app_glue

L'ultima riga indica al sistema di compilazione di creare questa libreria statica. Lo script ndk-build inserisce la libreria creata (libandroid_native_app_glue.a) nella directory obj generati durante il processo di compilazione. Per ulteriori informazioni sull'android_native_app_glue consulta la relativa intestazione android_native_app_glue.h e il .cfile di origine corrispondente.

$(call import-module,android/native_app_glue)

Per ulteriori informazioni sul file Android.mk, vedi Android.mk.

main.c

Questo file contiene essenzialmente l'intero programma.

I seguenti elementi corrispondono alle librerie, sia condivise che statiche, enumerato in Android.mk.

#include <EGL/egl.h>
#include <GLES/gl.h>


#include <android/sensor.h>
#include <android/log.h>
#include <android_native_app_glue>

La libreria android_native_app_glue chiama la seguente funzione: passando una struttura di stato predefinita. Funge anche da wrapper semplifica la gestione dei callback NativeActivity.

void android_main(struct android_app* state) {

Il programma quindi gestisce gli eventi in coda dalla libreria di glue. L'evento segue la struttura dello stato.

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;

L'applicazione si prepara ad iniziare a monitorare i sensori, utilizzando API in 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);

Poi ha inizio un ciclo, in cui l'applicazione esegue il polling del sistema i messaggi (eventi dei sensori). Invia messaggi a android_native_app_glue, che controlla se corrispondono qualsiasi evento onAppCmd definito in android_main. Quando una corrispondenza, il messaggio viene inviato al gestore per l'esecuzione.

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;
        }
    }

Quando la coda è vuota e il programma esce dal loop di polling, la il programma chiama OpenGL per disegnare lo schermo.

    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);
    }
}