Contoh: native-activity

Sampel native-activity berada di root sampel NDK, dalam folder native-activity. Ini merupakan contoh sangat sederhana dari aplikasi native murni, tanpa kode sumber Java. Meskipun tanpa sumber Java, compiler Java tetap membuat stub yang dapat dieksekusi untuk dijalankan oleh mesin virtual. Stub ini berfungsi sebagai wrapper untuk program native aktual, yang terletak dalam file .so.

Aplikasi ini sendiri hanya merender warna ke seluruh layar, lalu mengubah sebagian warna sebagai respons atas gerakan yang dideteksinya.

AndroidManifest.xml

Aplikasi yang hanya menggunakan kode native tidak boleh menetapkan API level Android di bawah 9, yang memperkenalkan class framework NativeActivity.

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

Baris berikut mendeklarasikan android:hasCode sebagai false, karena aplikasi ini hanya memiliki kode native tanpa Java.

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

Baris berikut mendeklarasikan class NativeActivity.

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

Terakhir, manifes menentukan android:value sebagai nama library bersama yang akan dibuat, tanpa inisial lib dan ekstensi .so. Nilai ini harus sama dengan nama LOCAL_MODULE di Android.mk.

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

Android.mk

File ini dimulai dengan menyediakan nama untuk library bersama yang akan dibuat.

LOCAL_MODULE    := native-activity

Lalu, mendeklarasikan nama file kode-sumber native:

LOCAL_SRC_FILES := main.c

Selanjutnya, file mencantumkan library eksternal yang akan digunakan oleh sistem build untuk membuat kode biner. Setiap nama library diawali dengan opsi -l (link-against).

  • log adalah library logging.
  • android mencakup API dukungan Android standar untuk NDK. Untuk informasi selengkapnya tentang API yang didukung oleh Android dan NDK, lihat Android NDK Native API.
  • EGL berkaitan dengan bagian khusus platform pada API grafis.
  • GLESv1_CM berkaitan dengan OpenGL ES, versi OpenGL untuk Android. Library ini bergantung pada EGL.

Untuk setiap library:

  • Nama file sebenarnya dimulai dengan lib, dan diakhiri dengan ekstensi .so. Misalnya, nama file sebenarnya untuk library log adalah liblog.so.
  • Library ini berada di direktori berikut, root NDK: <ndk>/platforms/android-<sdk_version>/arch-<abi>/usr/lib/.
LOCAL_LDLIBS    := -llog -landroid -lEGL -lGLESv1_CM

Baris berikutnya memberikan nama untuk library statis, android_native_app_glue, yang digunakan aplikasi untuk mengelola peristiwa siklus proses NativeActivity dan input sentuh.

LOCAL_STATIC_LIBRARIES := android_native_app_glue

Baris terakhir memberi tahu sistem build untuk membuat library statis ini. Skrip ndk-build menempatkan library yang dibuat (libandroid_native_app_glue.a) ke dalam direktori obj yang dibuat selama proses build. Untuk informasi selengkapnya tentang library android_native_app_glue, lihat header android_native_app_glue.h dan file sumber .c yang sesuai.

$(call import-module,android/native_app_glue)

Untuk informasi selengkapnya tentang file Android.mk, lihat Android.mk.

main.c

File ini pada intinya memuat keseluruhan program.

Baris include berikut berkaitan dengan library, baik bersama maupun statis, yang dienumerasi dalam Android.mk.

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


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

Library android_native_app_glue memanggil fungsi berikut, dan meneruskan struktur status yang telah ditetapkan ke sana. Library ini juga berfungsi sebagai wrapper yang menyederhanakan penanganan callback NativeActivity.

void android_main(struct android_app* state) {

Berikutnya, program menangani peristiwa yang diantrekan oleh library glue. Pengendali peristiwa mengikuti struktur status.

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;

Aplikasi bersiap untuk mulai memantau sensor, menggunakan API pada 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);

Kemudian, loop dimulai, dan aplikasi mengkueri sistem untuk mendapatkan informasi pesan (peristiwa sensor). Loop mengirimkan pesan ke android_native_app_glue, yang memeriksa untuk menentukan apakah pesan sesuai dengan peristiwa onAppCmd apa pun yang ditetapkan dalam android_main. Jika terdapat kecocokan, pesan dikirimkan ke pengendali untuk dieksekusi.

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

Setelah antrean kosong dan program keluar dari loop kueri, program kemudian memanggil OpenGL untuk menggambar layar.

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