示例:native-activity

native-activity 示例位于 NDK 安装根目录下的 samples/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.mkLOCAL_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);
        }
    }