المفاهيم

قبل البدء

يفترض هذا الدليل أنك على دراية بالمفاهيم المضمَّنة في البرمجة الأصلية وفي تطوير Android.

المقدّمة

يقدم هذا القسم شرحًا عالي المستوى لكيفية عمل NDK. يُعد NDK في Android مجموعة من الأدوات التي تتيح لك تضمين C أو C++ ("رمز أصلي") في تطبيقات Android. إنّ إمكانية استخدام الرموز البرمجية الأصلية في تطبيقات Android قد تكون مفيدة بشكل خاص للمطوّرين الذين يريدون تنفيذ أحد الإجراءات التالية:

  • نقل تطبيقاتهم بين المنصات.
  • إعادة استخدام المكتبات الحالية أو توفير مكتبات خاصة بها لإعادة استخدامها
  • تحسين الأداء في بعض الحالات، لا سيما الحالات التي تستخدم فيها الخوارزميات المكثفة مثل الألعاب.

طريقة العمل

يقدم هذا القسم المكونات الرئيسية المستخدمة في بناء تطبيق أصلي لنظام Android، ويتابع وصف عملية الإنشاء والتغليف.

المكونات الرئيسية

يجب أن تكون على دراية بالمكونات التالية أثناء إنشاء تطبيقك:

  • المكتبات المشتركة الأصلية: ينشئ NDK هذه المكتبات، أو ملفات .so، من رمز المصدر C/C++ لديك.

  • المكتبات الثابتة الأصلية: يمكن لنسخة NDK أيضًا إنشاء مكتبات ثابتة أو ملفات .a يمكنك ربطها بمكتبات أخرى.

  • واجهة Java الأصلية (JNI): واجهة JNI هي الواجهة التي تتواصل من خلالها مكوّنات Java وC++ مع بعضها. يفترض هذا الدليل معرفة المعهد الأمريكي للمعايير الوطنية (JNI)، وللحصول على معلومات حوله، يمكنك الاطّلاع على مواصفات واجهة Java الأصلية.

  • واجهة التطبيق الثنائية (ABI): تحدِّد واجهة التطبيق الثنائية (ABI) الطريقة التي من المتوقّع أن يتفاعل بها رمز الجهاز لتطبيقك مع النظام في وقت التشغيل. وينشئ NDK ملفات .so وفقًا لهذه التعريفات. تتوافق واجهات ABI المختلفة مع بُنى مختلفة: تتوافق NDK مع واجهة التطبيق الثنائية (ABI) مع ARM 32 بت وAArch64 وx86 وx86-64. لمزيد من المعلومات، يُرجى الاطّلاع على Android ABIS.

  • البيان: إذا كنت تكتب تطبيقًا لا يشتمل على مكوّن JavaScript، يجب الإفصاح عن فئة NativeActivity في البيان. راجِع استخدام واجهة original_activity.h للحصول على مزيد من التفاصيل حول كيفية إجراء ذلك.

التدفق

في ما يلي الخطوات التي يجب اتّباعها لتطوير تطبيق أصلي لنظام التشغيل Android:

  1. صمم تطبيقك، وحدد الأجزاء التي تريد تنفيذها في Java، والأجزاء التي سيتم تنفيذها كرموز برمجية أصلية.

  2. أنشِئ مشروعًا لتطبيق Android مثلما تفعل مع أي مشروع آخر على Android.

  3. إذا كنت تكتب تطبيقًا أصليًا فقط، يجب تحديد فئة NativeActivity في AndroidManifest.xml. لمزيد من المعلومات، يُرجى الاطّلاع على الأنشطة والتطبيقات الأصلية.

  4. أنشِئ ملف Android.mk يصف المكتبة الأصلية، بما في ذلك الاسم والعلامات والمكتبات المرتبطة وملفات المصدر التي سيتم تجميعها في دليل "JNI".

  5. يمكنك اختياريًا إنشاء ملف Application.mk من خلال ضبط عناصر ABIS المستهدفة وسلسلة الأدوات ووضع الإصدار/تصحيح الأخطاء وSTL. بالنسبة إلى أي مما لم تحدّده، يتم استخدام القيم الافتراضية التالية، على التوالي:

    • واجهة التطبيق الثنائية (ABI): جميع واجهات ABI غير المتوقفة
    • الوضع: إصدار
    • STL: نظام
  6. ضَع المصدر الأصلي ضِمن دليل jni للمشروع.

  7. استخدِم ndk-build لتجميع المكتبات الأصلية (.so و.a).

  8. أنشِئ مكوِّن Java، ثم أنشِئ ملف .dex القابل للتنفيذ.

  9. أنشئ حزمة كل شيء في ملف APK يحتوي على .so و.dex والملفات الأخرى اللازمة لتشغيل التطبيق.

الأنشطة والتطبيقات الأصلية

توفّر حزمة تطوير البرامج (SDK) لنظام التشغيل Android فئة مساعِدة، وهي NativeActivity، تتيح لك كتابة نشاط أصلي بالكامل. تعالج NativeActivity الاتصال بين إطار عمل Android والرموز البرمجية الأصلية، بحيث لا تحتاج إلى فئة فرعية أو استدعاء طرقها. ما عليك سوى تعريف تطبيقك بأنّه أصلي في ملف AndroidManifest.xml والبدء في إنشاء تطبيقك الأصلي.

لا يزال أحد تطبيقات Android الذي يستخدم NativeActivity يعمل في جهازه الافتراضي في وضع الحماية من التطبيقات الأخرى. وبالتالي يظل بإمكانك الوصول إلى واجهات برمجة التطبيقات لإطار عمل Android من خلال JNI. في بعض الحالات، مثل أدوات الاستشعار وأحداث الإدخال ومواد العرض، يوفّر NDK واجهات أصلية يمكنك استخدامها بدلاً من الحاجة إلى الاتصال عبر مؤشر JNI. لمزيد من المعلومات حول هذا الدعم، يُرجى الاطّلاع على واجهات برمجة التطبيقات الأصلية.

بغض النظر عما إذا كنت تقوم بتطوير نشاط أصلي أم لا، نوصي بإنشاء مشاريعك باستخدام الأدوات التقليدية لإصدار Android. ويساعد ذلك في ضمان إنشاء تطبيقات Android ووضع هيكلها بالهيكل الصحيح.

يوفّر لك Android NDK خيارين لتنفيذ نشاطك الأصلي:

  • يُحدِّد العنوان localized_activity.h الإصدار الأصلي لفئة NativeActivity. يحتوي على واجهة معاودة الاتصال وهياكل البيانات التي تحتاجها لإنشاء نشاطك الأصلي. بما أنّ سلسلة التعليمات الرئيسية لتطبيقك تعالج عمليات معاودة الاتصال، يجب ألا يتم حظر عمليات تنفيذ معاودة الاتصال. في حال حظره، قد تظهر لك أخطاء ANR (التطبيق لا يستجيب) لأنّ سلسلة التعليمات الرئيسية لا تستجيب حتى ظهور معاودة الاتصال.
  • يحدّد ملف android_native_app_glue.h مكتبة مساعد ثابتة تم إنشاؤها أعلى واجهة Native_activity.h. إنه ينتج سلسلة رسائل أخرى، والتي تعالج أشياء مثل استدعاءات الاتصال أو إدخال الأحداث في حلقة حدث. يؤدي نقل هذه الأحداث إلى سلسلة محادثات منفصلة إلى منع أي استدعاءات من حظر سلسلة التعليمات الرئيسية.

يتوفر مصدر <ndk_root>/sources/android/native_app_glue/android_native_app_glue.c أيضًا، ما يسمح لك بتعديل عملية التنفيذ.

لمزيد من المعلومات حول كيفية استخدام هذه المكتبة الثابتة، افحص نموذج تطبيق النشاط الأصلي ووثائقه. يمكنك الاطّلاع على محتوى إضافي في قسم التعليقات في ملف <ndk_root>/sources/android/native_app_glue/android_native_app_glue.h.

استخدام واجهة الإصدار الأصلي (Native_activity.h)

لتنفيذ نشاط مدمج مع المحتوى باستخدام واجهة localized_activity.h:

  1. أنشئ دليل jni/ في الدليل الجذري لمشروعك. يخزن هذا الدليل جميع التعليمات البرمجية الأصلية.

  2. يجب تعريف نشاطك الأصلي في ملف AndroidManifest.xml.

    بما أنّ تطبيقك لا يحتوي على رمز JavaScript، يمكنك ضبط android:hasCode على false.

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

    وعليك ضبط السمة android:name لعلامة النشاط على NativeActivity.

    <activity android:name="android.app.NativeActivity"
              android:label="@string/app_name">
    

    تحدد سمة android:value العلامة meta-data اسم المكتبة المشتركة التي تحتوي على نقطة الدخول إلى التطبيق (مثل C/C++ main)، مع حذف البادئة lib واللاحقة .so من المكتبة.

    <manifest>
      <application>
        <activity>
          <meta-data android:name="android.app.lib_name"
                     android:value="native-activity" />
          <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
        </activity>
      </application>
    </manifest>
    
  3. أنشئ ملفًا لنشاطك الأصلي، ونفِّذ الدالة المسماة في المتغير ANativeActivity_onCreate. يستدعي التطبيق هذه الدالة عند بدء النشاط الأصلي. هذه الدالة مشابهة للدالة main في لغة C/C++ ، تتلقّى مؤشرًا إلى بنية ANativeActivity التي تحتوي على مؤشرات الدالة لعمليات تنفيذ معاودة الاتصال المختلفة التي تحتاج إلى كتابتها. اضبط مؤشرات دالة معاودة الاتصال السارية في ANativeActivity->callbacks على عمليات تنفيذ عمليات الاستدعاء.

  4. اضبط الحقل ANativeActivity->instance على عنوان أي مثيل من بيانات محدّدة تريد استخدامها.

  5. قم بتنفيذ أي شيء آخر تريد أن يفعله نشاطك عند البدء.

  6. نفِّذ بقية عمليات معاودة الاتصال التي تضبطها في ANativeActivity->callbacks. لمزيد من المعلومات عن وقت استدعاء عمليات الاسترداد، راجِع إدارة دورة حياة النشاط.

  7. طوّر بقية تطبيقك.

  8. أنشئ Android.mk file في دليل jni/ لمشروعك لوصف وحدتك الأصلية لنظام الإصدار. لمزيد من المعلومات، يمكنك الاطّلاع على Android.mk.

  9. بعد إنشاء ملف Android.mk، يجب تجميع الرمز الأصلي باستخدام الأمر ndk-build.

    cd <path>/<to>/<project>
    $NDK/ndk-build
    
  10. أنشئ مشروع Android وثبِّته كالمعتاد. إذا كان رمزك البرمجي الأصلي متوفرًا في دليل jni/، سيحزم النص البرمجي للإصدار ملفات .so التي تم إنشاؤها منه تلقائيًا في حزمة APK.

رمز نموذجي إضافي

لتنزيل نماذج NDK، يمكنك الاطّلاع على عيّنات NDK.