يتوفّر نموذج النشاط الأصلي ضمن جذور نماذج NDK في المجلد
native-activity
. وهذا مثال بسيط جدًا على تطبيق أصلي
خالٍ، بدون رمز مصدر Java. في حال عدم توفُّر أي مصدر لـ Java، لا يزال
يُجمِع برنامج التجميع Java رمزًا تنفيذيًا للجهاز الافتراضي لتشغيله.
يعمل الرمز البديل كبرنامج تضمين للبرنامج الأصلي المضمّن في الملف .so
.
يعرض التطبيق نفسه لونًا على الشاشة بأكملها، ثم يغيّر اللون بشكل جزئي استجابةً للحركة التي يكتشفها.
ملف ManManifest.xml
ويجب ألا يحدّد التطبيق الذي يتضمّن رمزًا أصليًا فقط مستوى واجهة برمجة تطبيقات Android أقل من 9، ما أدى إلى تقديم فئة إطار العمل NativeActivity
.
<uses-sdk android:minSdkVersion="9" />
يعرّف السطر التالي android:hasCode
على أنه false
، لأن هذا التطبيق يحتوي على رمز أصلي فقط بدون JavaScript.
<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
(رابط مقابل) كل اسم مكتبة.
log
هي مكتبة تسجيل.- تشتمل واجهة برمجة التطبيقات
android
على واجهات برمجة تطبيقات Standard Android المتوافقة مع NDK. لمزيد من المعلومات حول واجهات برمجة التطبيقات التي يتوافق معها نظام Android وNDK، يُرجى الاطّلاع على واجهات برمجة تطبيقات NDK الأصلية على نظام التشغيل Android. - يتوافق
EGL
مع الجزء الخاص بالنظام الأساسي لواجهة برمجة التطبيقات للرسومات. - يتوافق
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.
major.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;
يتم تحضير التطبيق لبدء مراقبة أجهزة الاستشعار باستخدام واجهات برمجة التطبيقات في 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); } }