ตัวอย่างกิจกรรมดั้งเดิมอยู่ภายใต้
รูทตัวอย่าง 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 APIEGL
สอดคล้องกับส่วนเฉพาะแพลตฟอร์มของ 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); } }