ตัวอย่าง: Native-activity

ตัวอย่างกิจกรรมดั้งเดิมอยู่ภายใต้ รูทตัวอย่าง NDK ในโฟลเดอร์ native-activity นี่เป็นตัวอย่างง่ายๆ ของ โดยไม่มีซอร์สโค้ดของ Java ในกรณีที่ไม่มีซอร์ส Java ค่า คอมไพเลอร์ Java ยังคงสร้างสตับที่สั่งการได้เพื่อให้เครื่องเสมือนเรียกใช้ ต้นขั้วทำหน้าที่เป็น Wrapper ของโปรแกรมเนทีฟจริง ซึ่งอยู่ใน.so

ตัวแอปเองจะแสดงผลสีบนทั้งหน้าจอ แล้วเปลี่ยนสีเป็นบางส่วนตามการเคลื่อนไหวที่ตรวจจับได้

AndroidManifest.xml

แอปที่มีเฉพาะโค้ดแบบเนทีฟต้องไม่ระบุระดับ API ของ Android ที่ต่ำกว่า 9 คลาสเฟรมเวิร์กของ NativeActivity

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

บรรทัดต่อไปนี้ประกาศว่า android:hasCode เป็น false เนื่องจากแอปนี้มีเพียง โค้ดแบบเนทีฟ ไม่ใช่ Java

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

บรรทัดถัดไปคือการประกาศคลาส NativeActivity

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

สุดท้าย ไฟล์ Manifest ระบุว่า android:value เป็นชื่อของไลบรารีที่ใช้ร่วมกันที่จะ ที่สร้างขึ้น ลบ lib เริ่มต้นและส่วนขยาย .so ค่านี้ต้องเหมือนกับ ชื่อของ LOCAL_MODULE ใน Android.mk

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

Android mk

ไฟล์นี้เริ่มต้นด้วยการระบุชื่อไลบรารีที่ใช้ร่วมกันที่จะสร้าง

LOCAL_MODULE    := native-activity

ถัดไป จะประกาศชื่อไฟล์ซอร์สโค้ดดั้งเดิม

LOCAL_SRC_FILES := main.c

ถัดไป จะแสดงรายการไลบรารีภายนอกสำหรับระบบบิลด์ที่จะใช้ในการสร้างไบนารี มี -l ตัวเลือก (ลิงก์ต่อ) อยู่หน้าชื่อไลบรารีแต่ละรายการ

  • log เป็นไลบรารีการบันทึก
  • android ครอบคลุม API การสนับสนุนมาตรฐานของ Android สำหรับ NDK สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ API ที่ Android และ NDK รองรับ โปรดดู Android NDK Native API
  • EGL สอดคล้องกับส่วนเฉพาะแพลตฟอร์มของ API กราฟิก
  • GLESv1_CM สอดคล้องกับ OpenGL ES ซึ่งเป็นเวอร์ชันของ OpenGL สำหรับ Android ไลบรารีนี้ ขึ้นอยู่กับ EGL

สำหรับคลังแต่ละรายการ ให้ทำดังนี้

  • ชื่อไฟล์จริงจะขึ้นต้นด้วย lib และลงท้ายด้วย ส่วนขยาย .so เช่น ชื่อไฟล์จริงของ ไลบรารีของ log คือliblog.so
  • ไลบรารีอยู่ในไดเรกทอรีต่อไปนี้ คือราก NDK <ndk>/platforms/android-<sdk_version>/arch-<abi>/usr/lib/
LOCAL_LDLIBS    := -llog -landroid -lEGL -lGLESv1_CM

บรรทัดถัดไประบุชื่อไลบรารีแบบคงที่ android_native_app_glue ซึ่ง แอปพลิเคชันใช้เพื่อจัดการเหตุการณ์ในวงจรของ NativeActivity และการป้อนข้อมูลด้วยการสัมผัส

LOCAL_STATIC_LIBRARIES := android_native_app_glue

บรรทัดสุดท้ายจะบอกระบบบิลด์เพื่อสร้างไลบรารีแบบคงที่นี้ สคริปต์ ndk-build จะวางไลบรารีที่สร้าง (libandroid_native_app_glue.a) ลงในไดเรกทอรี obj ที่สร้างขึ้นระหว่างขั้นตอนบิลด์ ดูข้อมูลเพิ่มเติมเกี่ยวกับandroid_native_app_glue โปรดดูส่วนหัว android_native_app_glue.h และไฟล์ต้นฉบับ .c ที่เกี่ยวข้อง

$(call import-module,android/native_app_glue)

ดูข้อมูลเพิ่มเติมเกี่ยวกับไฟล์ Android.mk ได้ที่ Android.mk

Main.c

โดยพื้นฐานแล้วไฟล์นี้จะมีทั้ง Progam

รายการต่อไปนี้รวมถึงที่สอดคล้องกับไลบรารี ทั้งที่แชร์และแบบคงที่ แจกแจงในAndroid.mk

#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 เรียกฟังก์ชันต่อไปนี้ ผ่านโครงสร้างสถานะที่กำหนดไว้ล่วงหน้า และยังทำหน้าที่เป็น Wrapper ที่ จัดการ Callback ของ NativeActivity ได้ง่ายขึ้น

void android_main(struct android_app* state) {

ถัดไป โปรแกรมจะจัดการกับเหตุการณ์ที่จัดคิวโดย Glue Library กิจกรรม จะใช้โครงสร้างสถานะ

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;

แอปพลิเคชันกำลังเตรียมพร้อมเพื่อเริ่มตรวจสอบเซ็นเซอร์ โดยใช้ API ใน 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);

ถัดไป ลูปจะเริ่มขึ้น ซึ่งแอปพลิเคชันจะสำรวจระบบสำหรับ ข้อความ (เหตุการณ์เซ็นเซอร์) ส่งข้อความไปยัง android_native_app_glue ซึ่งจะตรวจสอบว่าตรงกันหรือไม่ เหตุการณ์ onAppCmd ใดๆ ที่กำหนดไว้ใน android_main เมื่อ ตรงกัน ข้อความจะส่งไปยังตัวจัดการเพื่อการดำเนินการ

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

เมื่อคิวว่างเปล่า และโปรแกรมออกจากลูปการสำรวจ โปรแกรมจะเรียก OpenGL ให้วาดหน้าจอ

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