範例:native-activity

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

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