نمونه 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); } }