native-activity 範例位於 NDK 範例根目錄的 native-activity
資料夾中。這是一個非常簡單的純原生應用程式範例,不包含 Java 原始碼。儘管沒有任何 Java 原始碼,Java 編譯器仍會建立可供虛擬機器執行的虛設常式。該虛設常式會是 .so
檔案中實際原生程式的包裝函式。
應用程式本身只會在整個螢幕上呈現一種顏色,然後根據偵測到的移動變更局部顏色。
AndroidManifest.xml
只有原生程式碼的應用程式不得指定級別 9 以下的 Android API 級別 (從級別 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"
最後,資訊清單會將 android:value
指定為要建構的共用程式庫的名稱 (刪掉開頭的 lib
和 .so
副檔名)。這個值必須與 Android.mk
中的 LOCAL_MODULE
名稱相同。
<meta-data android:name="android.app.lib_name" android:value="native-activity" />
Android.mk
這個檔案首先提供要產生的共用程式庫的名稱。
LOCAL_MODULE := native-activity
接著,它會宣告原生原始碼檔案的名稱。
LOCAL_SRC_FILES := main.c
接著,它會列出外部程式庫,供建構系統在建構二進位檔時使用。每個程式庫名稱前面會有 -l
(link-against) 選項。
log
是記錄程式庫。android
包含適用於 NDK 的標準 Android 支援 API。如要進一步瞭解 Android 和 NDK 支援的 API,請參閱 Android NDK 原生 API。EGL
對應圖形 API 的平台專屬部分。GLESv1_CM
對應 OpenGL ES (也就是適用於 Android 的 OpenGL 版本)。這個程式庫依附於 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
這個檔案基本上包含整個程式。
以下 include 對應 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
程式庫會呼叫下列函式,並傳遞預先定義的狀態結構給這些函式。這個程式庫也會做為包裝函式,簡化 NativeActivity
回呼處理作業。
void android_main(struct android_app* state) {
接著,程式會處理由 glue 程式庫排入佇列的事件。事件處理常式遵循狀態結構。
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;
應用程式準備開始使用 sensor.h
中的 API 監控感應器。
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
,由其檢查訊息是否與 android_main
中定義的任何 onAppCmd
事件相符。如果相符,訊息會傳送至處理常式並執行。
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); } }