النموذج: نشاط مدمج مع المحتوى

ويمكن العثور على عيّنة النشاط الأصلي ضمن جذر نماذج NDK، في المجلد native-activity إنه مثال بسيط للغاية لمنتج أصلي تمامًا بدون أي رمز مصدر Java. في حالة عدم وجود أي مصدر Java، لا يزال المحول البرمجي لـ Java ينشئ كعبًا قابلاً للتنفيذ لتشغيل الجهاز الافتراضي. ويكون التشعّب بمثابة برنامج تضمين للبرنامج الأصلي الفعلي المتوفّر في .so. الملف.

يعرض التطبيق نفسه لونًا على الشاشة بأكملها، ثم تغير اللون جزئيًا استجابة للحركة التي يتم اكتشافها.

ملف AndroidManifest.xml

بالنسبة إلى التطبيقات التي تتضمّن رمزًا برمجيًا أصليًا فقط، يجب ألّا تحدّد مستوى واجهة برمجة تطبيقات Android أقل من 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"

أخيرًا، يحدد البيان 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 واجهات برمجة التطبيقات العادية لدعم Android الخاصة بحِزمة NDK. لمزيد من المعلومات عن واجهات برمجة التطبيقات التي يدعمها Android وNDK، راجع Android NDK Native API.
  • تتوافق 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

ماين.ج

يحتوي هذا الملف بشكل أساسي على البرنامج بالكامل.

يتضمن ما يلي تتجاوب مع المكتبات، المشتركة والثابتة، تم تعدادها في 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);
    }
}