示例: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"
最后,清单会删掉开头的 lib
和末尾的 .so
扩展名,从而指定 android:value
作为要构建的共享库的名称。此值必须与 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
(链接)选项。
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 和/或其关联公司的注册商标。
最后更新时间 (UTC):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"]],["最后更新时间 (UTC):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```"]]