Mẫu: hoạt động gốc

Mẫu hoạt động gốc nằm trong thư mục gốc mẫu NDK, trong thư mục native-activity. Mẫu này là một ví dụ rất đơn giản về ứng dụng gốc thuần tuý không có mã nguồn Java. Trong trường hợp không có nguồn Java, trình biên dịch Java vẫn tạo một mã giả lập có thể thực thi để máy ảo chạy. Mã giả lập có vai trò là trình bao bọc cho chương trình gốc thực tế, nằm trong tệp .so.

Bản thân ứng dụng chỉ hiển thị một màu trên toàn bộ màn hình, sau đó thay đổi một phần màu đó để đáp ứng chuyển động mà ứng dụng phát hiện được.

AndroidManifest.xml

Một ứng dụng chỉ có mã gốc không được chỉ định cấp độ Android API thấp hơn 9, là cấp độ đưa ra lớp khung NativeActivity.

<uses-sdk android:minSdkVersion="9" />

Dòng sau đây khai báo android:hasCodefalse, vì ứng dụng này chỉ có mã gốc – không có Java.

<application android:label="@string/app_name"
android:hasCode="false">

Dòng tiếp theo khai báo lớp NativeActivity.

<activity android:name="android.app.NativeActivity"

Cuối cùng, tệp kê khai chỉ định android:value làm tên của thư viện dùng chung cần tạo, trừ lib ở đầu và đuôi .so. Giá trị này phải giống với tên của LOCAL_MODULE trong Android.mk.

<meta-data android:name="android.app.lib_name"
        android:value="native-activity" />

Android.mk

Tệp này bắt đầu bằng cách cung cấp tên của thư viện dùng chung cần tạo.

LOCAL_MODULE    := native-activity

Tiếp theo, tệp sẽ khai báo tên của tệp mã nguồn gốc.

LOCAL_SRC_FILES := main.c

Tiếp theo, tệp liệt kê các thư viện bên ngoài mà hệ thống xây dựng sử dụng để tạo tệp nhị phân. Tuỳ chọn -l (liên kết dựa trên) đứng trước tên mỗi thư viện.

  • log là một thư viện ghi nhật ký.
  • android bao gồm các API hỗ trợ Android tiêu chuẩn cho NDK. Để biết thêm thông tin về các API mà Android và NDK hỗ trợ, hãy xem API gốc của Android NDK.
  • EGL tương ứng với phần dành riêng cho từng nền tảng của API đồ hoạ.
  • GLESv1_CM tương ứng với OpenGL ES, phiên bản OpenGL cho Android. Thư viện này phụ thuộc vào EGL.

Đối với mỗi thư viện:

  • Tên tệp thực tế bắt đầu bằng lib và kết thúc bằng đuôi .so. Ví dụ: Tên tệp thực tế của thư viện logliblog.so.
  • Thư viện nằm trong thư mục sau trong thư mục gốc NDK: <ndk>/platforms/android-<sdk_version>/arch-<abi>/usr/lib/.
LOCAL_LDLIBS    := -llog -landroid -lEGL -lGLESv1_CM

Dòng tiếp theo cung cấp tên của thư viện tĩnh, android_native_app_glue, mà ứng dụng sử dụng để quản lý phương thức nhập bằng cách chạm và sự kiện trong vòng đời của NativeActivity.

LOCAL_STATIC_LIBRARIES := android_native_app_glue

Dòng cuối cùng yêu cầu hệ thống xây dựng tạo thư viện tĩnh này. Tập lệnh ndk-build đặt thư viện đã tạo (libandroid_native_app_glue.a) vào thư mục obj được tạo trong quy trình xây dựng. Để biết thêm thông tin về thư viện android_native_app_glue, hãy xem tiêu đề android_native_app_glue.h và tệp nguồn .c tương ứng.

$(call import-module,android/native_app_glue)

Để biết thêm thông tin về tệp Android.mk, hãy xem bài viết Android.mk.

main.c

Tệp này về cơ bản chứa toàn bộ chương trình.

Dữ liệu bao gồm (include) sau tương ứng với các thư viện, cả thư viện dùng chung và tĩnh, được liệt kê trong Android.mk.

#include <EGL/egl.h>
#include <GLES/gl.h>

#include <android/sensor.h>
#include <android/log.h>
#include <android_native_app_glue>

Thư viện android_native_app_glue gọi hàm sau, truyền cho hàm đó cấu trúc trạng thái đã xác định trước. Thư viện này cũng đóng vai trò là một trình bao bọc giúp đơn giản hoá việc xử lý các lệnh gọi lại NativeActivity.

void android_main(struct android_app* state) {

Tiếp theo, chương trình sẽ xử lý các sự kiện được đưa thư viện keo (glue) vào hàng đợi. Trình xử lý sự kiện tuân theo cấu trúc trạng thái.

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;

Ứng dụng chuẩn bị bắt đầu giám sát các cảm biến, bằng cách sử dụng API trong sensor.h.

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

Tiếp theo, một vòng lặp bắt đầu, trong đó ứng dụng thăm dò hệ thống để biết các thông báo (sự kiện cảm biến). Ứng dụng gửi thông báo đến android_native_app_glue và đối tượng này sẽ kiểm tra xem liệu thông báo có khớp với sự kiện onAppCmd nào được xác định trong android_main hay không. Khi có kết quả trùng khớp, thông báo sẽ được gửi cho trình xử lý thực thi.

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

Sau khi hàng đợi trống và chương trình thoát khỏi vòng lặp thăm dò, chương trình sẽ gọi OpenGL để vẽ màn hình.

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