Esempio: attività nativa

Il campione dell'attività nativa si trova nella directory principale degli esempi NDK, nella cartella native-activity. È un esempio molto semplice di applicazione puramente nativa, senza codice sorgente Java. In assenza di un'origine Java, il compilatore Java crea comunque uno stub eseguibile che la macchina virtuale può eseguire. Lo stub funge da wrapper per il programma nativo effettivo, che si trova nel file .so.

L'app stessa mostra semplicemente un colore sull'intero schermo per poi cambiarne parzialmente il colore in risposta al movimento rilevato.

File AndroidManifest.xml

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

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

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

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

Nella riga successiva viene dichiarata la classe NativeActivity.

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

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

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

Android.mk

Per iniziare, fornisci 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 che il sistema di compilazione può utilizzare per la creazione del programma binario. L'opzione -l (link against) precede il nome di ogni libreria.

  • log è una libreria di logging.
  • android include le API di supporto Android standard per NDK. Per ulteriori informazioni sulle API supportate da Android e NDK, consulta la sezione API native NDK di Android.
  • 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 biblioteca:

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

La riga successiva indica il nome della libreria statica, android_native_app_glue, che l'applicazione utilizza per gestire gli eventi del ciclo di vita di NativeActivity e l'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 generata durante il processo di compilazione. Per ulteriori informazioni sulla libreria android_native_app_glue, consulta l'intestazione android_native_app_glue.h e il file di origine .ccorrispondente.

$(call import-module,android/native_app_glue)

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

principale.c

Questo file contiene essenzialmente l'intero programma.

Quanto segue include le librerie, sia condivise che statiche, enumerate 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, inviandola una struttura di stato predefinita. Serve anche da wrapper che semplifica la gestione dei callback NativeActivity.

void android_main(struct android_app* state) {

Successivamente, il programma gestisce gli eventi in coda dalla libreria di colla. Il gestore di eventi 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 a iniziare a monitorare i sensori, utilizzando le 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);

Successivamente, inizia un loop, in cui l'applicazione esegue il polling del sistema per trovare messaggi (eventi dei sensori). Invia messaggi a android_native_app_glue, che controlla se corrispondono a eventi onAppCmd definiti in android_main. Quando si verifica 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, 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);
    }
}