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 librerialog
è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 .c
corrispondente.
$(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); } }