Örnek: yerel-etkinlik

Yerel etkinlik örneği, native-activity klasöründe NDK örnekleri kökünün altında bulunur. Bu, Java kaynak kodu içermeyen, tamamen yerel bir uygulama örneğidir. Herhangi bir Java kaynağı olmadığında Java derleyicisi yine de sanal makinenin çalışması için yürütülebilir bir saplama oluşturur. Saplama, .so dosyasında yer alan gerçek yerel program için sarmalayıcı görevi görür.

Uygulamanın kendisi sadece tüm ekranda bir renk oluşturur ve algıladığı harekete yanıt olarak rengi kısmen değiştirir.

AndroidManifest.xml

Yalnızca yerel koda sahip bir uygulama, NativeActivity çerçeve sınıfını kullanıma sunan 9'dan düşük bir Android API düzeyi belirtmemelidir.

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

Bu uygulama yalnızca yerel kod içerdiği, Java olmadığı için aşağıdaki satırda android:hasCode değeri false olarak tanımlanmaktadır.

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

Sonraki satırda NativeActivity sınıfı tanımlanır.

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

Son olarak manifest, oluşturulan paylaşılan kitaplığın adı olarak başlangıçtaki lib ve .so uzantısı çıkarılarak android:value değerini belirtir. Bu değer, Android.mk dilindeki LOCAL_MODULE adıyla aynı olmalıdır.

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

Android.mk

Bu dosya, oluşturulacak paylaşılan kitaplığın adını sağlayarak başlar.

LOCAL_MODULE    := native-activity

Daha sonra, yerel kaynak kodu dosyasının adını bildirir.

LOCAL_SRC_FILES := main.c

Daha sonra, derleme sisteminin ikili programı oluştururken kullanacağı harici kitaplıkları listeler. -l (link-against) seçeneği her kitaplık adından önce gelir.

  • log, günlük kaydı kitaplığıdır.
  • android, NDK için standart Android destek API'lerini kapsar. Android ve NDK'nın desteklediği API'ler hakkında daha fazla bilgi için Android NDK Yerel API'leri bölümüne bakın.
  • EGL, grafik API'sinin platforma özel bölümüne karşılık gelir.
  • GLESv1_CM, Android için OpenGL ES'ye karşılık gelir. Bu kitaplık EGL'yi temel alır.

Her kitaplık için:

  • Asıl dosya adı lib ile başlar ve .so uzantısıyla biter. Örneğin, log kitaplığının gerçek dosya adı liblog.so şeklindedir.
  • Kitaplık şu dizinde yer alır: NDK kök: <ndk>/platforms/android-<sdk_version>/arch-<abi>/usr/lib/.
LOCAL_LDLIBS    := -llog -landroid -lEGL -lGLESv1_CM

Sonraki satırda, uygulamanın NativeActivity yaşam döngüsü etkinliklerini ve dokunma girişini yönetmek için kullandığı android_native_app_glue statik kitaplığın adı verilir.

LOCAL_STATIC_LIBRARIES := android_native_app_glue

Son satır, derleme sistemine bu statik kitaplığı derlemesini bildirir. ndk-build komut dosyası, derlenen kitaplığı (libandroid_native_app_glue.a), derleme işlemi sırasında oluşturulan obj dizinine yerleştirir. android_native_app_glue kitaplığı hakkında daha fazla bilgi için android_native_app_glue.h başlığına ve ilgili .ckaynak dosyasına bakın.

$(call import-module,android/native_app_glue)

Android.mk dosyası hakkında daha fazla bilgi için Android.mk adresine bakın.

ana.c

Bu dosya esasen programın tamamını içerir.

Aşağıdakiler, Android.mk içinde numaralanan hem paylaşılan hem de statik kitaplıklara karşılık gelir.

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


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

android_native_app_glue kitaplığı, aşağıdaki işlevi çağırarak önceden tanımlanmış bir durum yapısı atar. Ayrıca NativeActivity geri çağırmalarının işlenmesini basitleştiren bir sarmalayıcı işlevi görür.

void android_main(struct android_app* state) {

Daha sonra program, yapışkan kitaplık tarafından sıraya alınan etkinlikleri işler. Etkinlik işleyici, durum yapısını izler.

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;

Uygulama, sensor.h API'lerini kullanarak sensörleri izlemeye hazırlanıyor.

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

Daha sonra bir döngü başlar. Bu döngüde uygulama, mesajlar (sensör etkinlikleri) için sistemi yoklar. android_native_app_glue adlı CSS'ye mesaj gönderir. Böylece, mesajların android_main içinde tanımlanan herhangi bir onAppCmd etkinliğiyle eşleşip eşleşmediğini kontrol eder. Bir eşleşme gerçekleştiğinde mesaj, yürütme için işleyiciye gönderilir.

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

Sıra boş olduğunda ve program yoklama döngüsünden çıktığında, program ekranı çizmek için OpenGL'i çağırır.

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