範例: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);
}
}
這個頁面中的內容和程式碼範例均受《內容授權》中的授權所規範。Java 與 OpenJDK 是 Oracle 和/或其關係企業的商標或註冊商標。
上次更新時間:2025-07-26 (世界標準時間)。
[[["容易理解","easyToUnderstand","thumb-up"],["確實解決了我的問題","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["缺少我需要的資訊","missingTheInformationINeed","thumb-down"],["過於複雜/步驟過多","tooComplicatedTooManySteps","thumb-down"],["過時","outOfDate","thumb-down"],["翻譯問題","translationIssue","thumb-down"],["示例/程式碼問題","samplesCodeIssue","thumb-down"],["其他","otherDown","thumb-down"]],["上次更新時間:2025-07-26 (世界標準時間)。"],[],[],null,["# Sample: native-activity\n\nThe native-activity sample resides under the\n[NDK samples root](https://github.com/android/ndk-samples/), in folder\n`native-activity`. It is a very simple example of a purely native\napplication, with no Java source code. In the absence of any Java source, the\nJava compiler still creates an executable stub for the virtual machine to run.\nThe stub serves as a wrapper for the actual, native program, which is located in the `.so`\nfile.\n\nThe app itself simply renders a color onto the entire screen, and\nthen changes the color partly in response to movement that it detects.\n\nAndroidManifest.xml\n-------------------\n\nAn app with only native code must not specify an Android API level lower than 9, which introduced\nthe [`NativeActivity`](/ndk/guides/concepts#naa) framework class. \n\n```xml\n\u003cuses-sdk android:minSdkVersion=\"9\" /\u003e\n```\n\nThe following line declares `android:hasCode` as `false`, as this app has only\nnative code--no Java. \n\n```xml\n\u003capplication android:label=\"@string/app_name\"\nandroid:hasCode=\"false\"\u003e\n```\n\nThe next line declares the `NativeActivity` class. \n\n```xml\n\u003cactivity android:name=\"android.app.NativeActivity\"\n```\n\nFinally, the manifest specifies `android:value` as the name of the shared library to be\nbuilt, minus the initial `lib` and the `.so` extension. This value must be the same as\nthe name of `LOCAL_MODULE` in `Android.mk`. \n\n```xml\n\u003cmeta-data android:name=\"android.app.lib_name\"\n android:value=\"native-activity\" /\u003e\n```\n\nAndroid.mk\n----------\n\nThis file begins by providing the name of the shared library to generate. \n\n```\nLOCAL_MODULE := native-activity\n```\n\nNext, it declares the name of the native source-code file. \n\n```\nLOCAL_SRC_FILES := main.c\n```\n\nNext, it lists the external libraries for the build system to use in building the binary. The\n`-l` (link-against) option precedes each library name.\n\n- `log` is a logging library.\n- `android` encompasses the standard Android support APIs for NDK. For more information about the APIs that Android and the NDK support, see [Android NDK Native\n APIs](/ndk/guides/stable_apis).\n- `EGL` corresponds to the platform-specific portion of the graphics API.\n- `GLESv1_CM` corresponds to OpenGL ES, the version of OpenGL for Android. This library depends on EGL.\n\nFor each library:\n\n- The actual file name starts with `lib`, and ends with the `.so` extension. For example, the actual file name for the `log` library is `liblog.so`.\n- The library resides in the following directory, NDK root: `\u003cndk\u003e/platforms/android-\u003csdk_version\u003e/arch-\u003cabi\u003e/usr/lib/`.\n\n```\nLOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM\n```\n\nThe next line provides the name of the static library, `android_native_app_glue`, which the\napplication uses to manage `NativeActivity` lifecycle events and touch input. \n\n```\nLOCAL_STATIC_LIBRARIES := android_native_app_glue\n```\n\nThe final line tells the build system to build this static library.\nThe `ndk-build` script places the built library\n(`libandroid_native_app_glue.a`) into the `obj` directory\ngenerated during the build process. For more information about the `android_native_app_glue`\nlibrary, see its `android_native_app_glue.h` header and corresponding `.c`source file. \n\n```\n$(call import-module,android/native_app_glue)\n```\n\nFor more information about the `Android.mk` file, see\n[Android.mk](/ndk/guides/android_mk).\n\nmain.c\n------\n\nThis file essentially contains the entire progam.\n\nThe following includes correspond to the libraries, both shared and static,\nenumerated in `Android.mk`. \n\n```c++\n#include \u003cEGL/egl.h\u003e\n#include \u003cGLES/gl.h\u003e\n\n\n#include \u003candroid/sensor.h\u003e\n#include \u003candroid/log.h\u003e\n#include \u003candroid_native_app_glue\u003e\n```\n\nThe `android_native_app_glue` library calls the following function,\npassing it a predefined state structure. It also serves as a wrapper that\nsimplifies handling of `NativeActivity` callbacks. \n\n```c++\nvoid android_main(struct android_app* state) {\n```\n\nNext, the program handles events queued by the glue library. The event\nhandler follows the state structure. \n\n```c++\nstruct engine engine;\n\n\n\n// Suppress link-time optimization that removes unreferenced code\n// to make sure glue isn't stripped.\napp_dummy();\n\n\nmemset(&engine, 0, sizeof(engine));\nstate-\u003euserData = &engine;\nstate-\u003eonAppCmd = engine_handle_cmd;\nstate-\u003eonInputEvent = engine_handle_input;\nengine.app = state;\n```\n\nThe application prepares to start monitoring the sensors, using the\nAPIs in `sensor.h`. \n\n```c++\n engine.sensorManager = ASensorManager_getInstance();\n engine.accelerometerSensor =\n ASensorManager_getDefaultSensor(engine.sensorManager,\n ASENSOR_TYPE_ACCELEROMETER);\n engine.sensorEventQueue =\n ASensorManager_createEventQueue(engine.sensorManager,\n state-\u003elooper, LOOPER_ID_USER, NULL, NULL);\n```\n\nNext, a loop begins, in which the application polls the system for\nmessages (sensor events). It sends messages to\n`android_native_app_glue`, which checks to see whether they match\nany `onAppCmd` events defined in `android_main`. When a\nmatch occurs, the message is sent to the handler for execution. \n\n```c++\nwhile (1) {\n // Read all pending events.\n int ident;\n int events;\n struct android_poll_source* source;\n\n\n // If not animating, we will block forever waiting for events.\n // If animating, we loop until all events are read, then continue\n // to draw the next frame of animation.\n while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL,\n &events,\n (void**)&source)) \u003e= 0) {\n\n\n // Process this event.\n if (source != NULL) {\n source-\u003eprocess(state, source);\n }\n\n\n // If a sensor has data, process it now.\n if (ident == LOOPER_ID_USER) {\n if (engine.accelerometerSensor != NULL) {\n ASensorEvent event;\n while (ASensorEventQueue_getEvents(engine.sensorEventQueue,\n &event, 1) \u003e 0) {\n LOGI(\"accelerometer: x=%f y=%f z=%f\",\n event.acceleration.x, event.acceleration.y,\n event.acceleration.z);\n }\n }\n }\n\n\n // Check if we are exiting.\n if (state-\u003edestroyRequested != 0) {\n engine_term_display(&engine);\n return;\n }\n }\n```\n\nOnce the queue is empty, and the program exits the polling loop, the\nprogram calls OpenGL to draw the screen. \n\n```c++\n if (engine.animating) {\n // Done with events; draw next animation frame.\n engine.state.angle += .01f;\n if (engine.state.angle \u003e 1) {\n engine.state.angle = 0;\n }\n\n\n // Drawing is throttled to the screen update rate, so there\n // is no need to do timing here.\n engine_draw_frame(&engine);\n }\n}\n```"]]