نمونه: بومی-فعالیت

نمونه Native-activity در زیر ریشه نمونه‌های NDK ، در پوشه native-activity قرار دارد. این یک مثال بسیار ساده از یک برنامه کاملا بومی است، بدون کد منبع جاوا. در غیاب هیچ منبع جاوا، کامپایلر جاوا هنوز یک خرد اجرایی برای ماشین مجازی ایجاد می کند. خرد به عنوان یک بسته بندی برای برنامه واقعی و بومی که در فایل .so قرار دارد عمل می کند.

خود برنامه به سادگی یک رنگ را بر روی کل صفحه نمایش می دهد و سپس رنگ را تا حدی در پاسخ به حرکتی که تشخیص می دهد تغییر می دهد.

AndroidManifest.xml

برنامه‌ای که فقط کد بومی دارد نباید سطح API Android کمتر از 9 را که کلاس چارچوب NativeActivity را معرفی کرده است، مشخص کند.

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

خط زیر android:hasCode را false اعلام می کند، زیرا این برنامه فقط کد بومی دارد – بدون جاوا.

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

خط بعدی کلاس NativeActivity را اعلام می کند.

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

در نهایت، مانیفست android:value به عنوان نام کتابخانه مشترکی که قرار است ساخته شود، منهای lib اولیه و پسوند .so مشخص می کند. این مقدار باید با نام LOCAL_MODULE در Android.mk یکسان باشد.

<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 شامل APIهای استاندارد پشتیبانی اندروید برای NDK است. برای اطلاعات بیشتر درباره APIهایی که Android و NDK پشتیبانی می‌کنند، به APIهای بومی Android NDK مراجعه کنید.
  • EGL مربوط به بخش مخصوص پلتفرم از API گرافیکی است.
  • GLESv1_CM مربوط به OpenGL ES، نسخه OpenGL برای Android است. این کتابخانه به 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

این فایل اساسا شامل کل برنامه است.

موارد زیر شامل کتابخانه‌های مشترک و ایستا است که در 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) {

در مرحله بعد، برنامه رویدادهایی را که توسط کتابخانه چسب در صف قرار می گیرند مدیریت می کند. کنترل کننده رویداد از ساختار حالت پیروی می کند.

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;

برنامه برای شروع نظارت بر حسگرها، با استفاده از APIها در 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);

بعد، یک حلقه شروع می شود، که در آن برنامه از سیستم برای پیام ها (رویدادهای حسگر) نظرسنجی می کند. این پیام‌ها را به android_native_app_glue می‌فرستد، که بررسی می‌کند که آیا با رویدادهای onAppCmd ​​تعریف‌شده در android_main مطابقت دارند یا خیر. هنگامی که یک تطابق رخ می دهد، پیام برای اجرا به کنترل کننده ارسال می شود.

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