يتم عادةً إنشاء تطبيقات Android باستخدام نظام الإنشاء Gradle. قبل التعرّف على تفاصيل كيفية ضبط إعدادات الإصدار، سنستكشف المفاهيم الأساسية المتعلقة بالإصدار حتى تتمكّن من فهم النظام بشكل كامل.
ما هو الإصدار؟
يحوّل نظام التصميم رمز المصدر إلى تطبيق قابل للتنفيذ. تتضمّن عمليات الإنشاء غالبًا أدوات متعددة لتحليل تطبيقك أو مكتبتك وتجميعها وربطها وتعبئتها. يستخدم Gradle أسلوبًا مستندًا إلى المهام لتنظيم هذه الأوامر وتنفيذها.
تتضمّن المهام أوامر تحوّل المدخلات إلى مخرجات. تحدّد الإضافات المهام وإعداداتها. يؤدي تطبيق إضافة على عملية الإنشاء إلى تسجيل مهامها وربطها ببعضها البعض باستخدام مدخلاتها ومخرجاتها. على سبيل المثال، سيؤدي تطبيق المكوّن الإضافي لنظام Gradle المتوافق مع Android (AGP) على ملف التصميم إلى تسجيل جميع المهام اللازمة لإنشاء حزمة APK أو مكتبة Android. يتيح لك المكوّن الإضافي java-library إنشاء ملف jar من رمز مصدر Java. تتوفّر مكوّنات إضافية مشابهة للغة Kotlin ولغات أخرى، ولكن المكوّنات الإضافية الأخرى تهدف إلى توسيع نطاق المكوّنات الإضافية. على سبيل المثال، تم تصميم المكوّن الإضافي protobuf لإضافة توافق protobuf إلى المكوّنات الإضافية الحالية، مثل AGP أو java-library.
يُفضّل Gradle الاتفاقية على الضبط، لذا ستتضمّن المكوّنات الإضافية قيمًا تلقائية جيدة فور تثبيتها، ولكن يمكنك ضبط الإصدار بشكل أكبر من خلال لغة خاصة بالمجال (DSL) تعريفية. تم تصميم لغة DSL بحيث يمكنك تحديد ما تريد إنشاءه، بدلاً من كيفية إنشائه. تتولّى المنطقية في المكوّنات الإضافية إدارة "كيفية" تنفيذ الإجراء. يتم تحديد هذا الإعداد في عدة ملفات إنشاء ضمن مشروعك (والمشاريع الفرعية).
يمكن أن تكون مدخلات المهام ملفات وأدلة بالإضافة إلى معلومات أخرى مشفّرة كأنواع Java (أعداد صحيحة أو سلاسل أو فئات مخصّصة). يمكن أن تكون النتائج عبارة عن دليل أو ملفات فقط، لأنّه يجب كتابتها على القرص. يؤدي ربط مخرجات مهمة بمدخلات مهمة أخرى إلى ربط المهام معًا، ما يعني أنّه يجب تنفيذ إحدى المهام قبل الأخرى.
على الرغم من أنّ Gradle يتيح كتابة رموز برمجية عشوائية وتعريفات مهام في ملفات الإنشاء، قد يؤدي ذلك إلى صعوبة فهم الأدوات لعملية الإنشاء وصعوبة صيانتها. على سبيل المثال، يمكنك كتابة اختبارات للرموز البرمجية داخل المكوّنات الإضافية، ولكن ليس في ملفات الإنشاء. بدلاً من ذلك، عليك حصر منطق الإنشاء وتعريفات المهام في المكوّنات الإضافية (التي تحدّدها أنت أو شخص آخر) وتحديد طريقة استخدام هذا المنطق في ملفات الإنشاء.
ماذا يحدث عند تنفيذ إصدار Gradle؟
يتم تنفيذ عمليات إنشاء Gradle على ثلاث مراحل. ينفّذ كل من هذه المراحل أجزاء مختلفة من الرمز الذي تحدّده في ملفات التصميم.
- تحدّد عملية التهيئة المشاريع والمشاريع الفرعية التي سيتم تضمينها في الإصدار، كما تعمل على إعداد مسارات الفئات التي تحتوي على ملفات الإصدار والمكوّنات الإضافية المستخدَمة. تركّز هذه المرحلة على ملف إعدادات تحدّد فيه المشاريع التي تريد إنشاءها والمواقع التي تريد استرداد المكوّنات الإضافية والمكتبات منها.
- تسجّل عملية الإعداد مهامًا لكل مشروع، وتنفّذ ملف الإصدار لتطبيق مواصفات الإصدار الخاصة بالمستخدم. من المهم معرفة أنّ رمز الإعداد لن يتمكّن من الوصول إلى البيانات أو الملفات التي تم إنشاؤها أثناء التنفيذ.
- تنفّذ عملية التنفيذ عملية "إنشاء" تطبيقك الفعلية. يكون الناتج من عملية الإعداد رسمًا بيانيًا موجّهًا غير دوري (DAG) للمهام، يمثّل جميع خطوات الإنشاء المطلوبة التي طلبها المستخدم (المهام المقدَّمة في سطر الأوامر أو كقيم تلقائية في ملفات الإنشاء). يمثّل هذا الرسم البياني العلاقة بين المهام، سواء كانت صريحة في تعريف المهمة أو مستندة إلى مدخلاتها ومخرجاتها. إذا كانت إحدى المهام تتضمّن مدخلاً هو مخرج مهمة أخرى، يجب تنفيذها بعد المهمة الأخرى. تنفّذ هذه المرحلة المهام القديمة بالترتيب المحدّد في الرسم البياني، وإذا لم تتغيّر مدخلات إحدى المهام منذ آخر تنفيذ لها، سيتخطّاها Gradle.
لمزيد من المعلومات، اطّلِع على مراحل نشاط الإصدار في Gradle.
لغات DSL الخاصة بالإعدادات
يستخدم Gradle لغة خاصة بالنطاق (DSL) لإعداد عمليات الإنشاء. يركّز هذا الأسلوب التقريري على تحديد البيانات بدلاً من كتابة تعليمات مفصّلة (إلزامية). يمكنك كتابة ملفات الإصدار باستخدام Kotlin أو Groovy، ولكن ننصحك بشدة باستخدام Kotlin.
تحاول لغات DSL تسهيل مشاركة الجميع، سواء خبراء المجال أو المبرمجين، في مشروع ما، وذلك من خلال تحديد لغة صغيرة تمثّل البيانات بطريقة أكثر طبيعية. يمكن أن توسّع مكوّنات Gradle الإضافية لغة DSL من أجل ضبط البيانات التي تحتاجها لمهامها.
على سبيل المثال، قد يبدو ضبط جزء Android من الإصدار على النحو التالي:
Kotlin
android { namespace = "com.example.app" compileSdk { version = release(36) { minorApiLevel = 1 } } // ... defaultConfig { applicationId = "com.example.app" minSdk { version = release(23) } targetSdk { version = release(36) } // ... } }
أنيق
android { namespace = 'com.example.app' compileSdk { version = release(36) { minorApiLevel = 1 } } // ... defaultConfig { applicationId = 'com.example.app' minSdk { version = release(23) } targetSdk { version = release(36) } // ... } }
وراء الكواليس، يشبه رمز DSL ما يلي:
fun Project.android(configure: ApplicationExtension.() -> Unit) {
...
}
interface ApplicationExtension {
var namespace: String?
fun compileSdk(configure: CompileSdkSpec.() -> Unit) {
...
}
val defaultConfig: DefaultConfig
fun defaultConfig(configure: DefaultConfig.() -> Unit) {
...
}
}
يتم تمثيل كل كتلة في DSL بدالة تأخذ تعبير lambda لتكوينها، وسمة تحمل الاسم نفسه للوصول إليها. ويجعل ذلك الرمز البرمجي في ملفات الإنشاء يبدو أشبه بمواصفات البيانات.
المهام التابعة الخارجية
قدّم نظام التصميم Maven مواصفات الاعتمادية ونظام التخزين والإدارة. يتم تخزين المكتبات في مستودعات (خوادم أو أدلة)، مع بيانات وصفية تتضمّن إصدارها والتبعيات على مكتبات أخرى. تحدّد المستودعات التي تريد البحث فيها، وإصدارات التبعيات التي تريد استخدامها، ويقوم نظام التصميم بتنزيلها أثناء عملية الإنشاء.
يتم تحديد عناصر Maven Artifacts حسب اسم المجموعة (الشركة أو المطوّر أو غير ذلك) واسم العنصر (اسم المكتبة) وإصدار هذا العنصر. ويتم تمثيل ذلك عادةً على النحو التالي: group:artifact:version.
يؤدي هذا الأسلوب إلى تحسين إدارة الإصدارات بشكل كبير. غالبًا ما يُطلق على هذه المستودعات اسم "مستودعات Maven"، ولكن الأمر يتعلق بطريقة تجميع ونشر العناصر. وقد تمت إعادة استخدام هذه المستودعات وبيانات التعريف في العديد من أنظمة الإنشاء، بما في ذلك Gradle (ويمكن لـ Gradle النشر في هذه المستودعات). تتيح المستودعات العامة مشاركة المحتوى ليستخدمه الجميع، بينما تحتفظ مستودعات الشركة بالتبعيات الداخلية داخل الشركة.
يمكنك أيضًا تقسيم مشروعك إلى مشاريع فرعية (تُعرف أيضًا باسم "الوحدات" في "استوديو Android")، والتي يمكن استخدامها أيضًا كعناصر تابعة. ينتج كل مشروع فرعي مخرجات (مثل ملفات JAR) يمكن أن تستهلكها المشاريع الفرعية أو مشروعك ذو المستوى الأعلى. ويمكن أن يؤدي ذلك إلى تحسين مدّة التصميم من خلال تحديد الأجزاء التي يجب إعادة إنشائها، بالإضافة إلى الفصل بشكل أفضل بين المسؤوليات في التطبيق.
سنشرح بالتفصيل كيفية تحديد الاعتماديات في مقالة إضافة اعتماديات الإصدار.
تنويعات الإصدار
عند إنشاء تطبيق Android، من المفترض أنك تريد إنشاء عدة إصدارات. تحتوي المتغيرات على رموز برمجية مختلفة أو يتم إنشاؤها باستخدام خيارات مختلفة، وهي تتألف من أنواع الإصدارات وصيغ المنتجات.
تختلف أنواع الإصدارات عن خيارات الإصدارات المحدّدة. يُعدِّد AGP تلقائيًا نوعَي الإصدار "إصدار" و "تصحيح أخطاء"، ولكن يمكنك تعديلهما وإضافة المزيد (ربما للتجربة أو الاختبار الداخلي).
لا يقلّل إصدار مخصص لتصحيح الأخطاء حجم تطبيقك أو يشوّشه، ما يؤدي إلى تسريع عملية إنشائه والحفاظ على جميع الرموز كما هي. كما يضع علامة "قابل للتصحيح" على التطبيق، ويوقّعه باستخدام مفتاح تصحيح أخطاء عام ويتيح الوصول إلى ملفات التطبيق المثبَّتة على الجهاز. يتيح ذلك استكشاف البيانات المحفوظة في الملفات وقواعد البيانات أثناء تشغيل التطبيق.
تعمل بنية الإصدار على تحسين التطبيق وتوقيعه باستخدام مفتاح الإصدار وحماية ملفات التطبيق المثبَّتة.
باستخدام صيغ المنتج، يمكنك تغيير المصدر المضمّن وأنواع التبعيات للتطبيق. على سبيل المثال، قد تحتاج إلى إنشاء إصدارَين من تطبيقك، أحدهما "تجريبي" والآخر "كامل"، أو ربما إصدارَين "مجاني" و "مدفوع". يمكنك كتابة الرمز البرمجي للمصدر المشترك في دليل مجموعة رموز المصدر "الرئيسي"، وتجاوز المصدر أو إضافته في مجموعة رموز المصدر تحمل اسم الصيغة.
ينشئ AGP صيغًا لكل مجموعة من نوع التصميم وصيغة المنتج. في حال عدم تحديد صيغ، سيتم تسمية التنويعات حسب أنواع الإصدارات. في حال تحديد كليهما، سيتم تسمية الصيغة <flavor><Buildtype>. على سبيل المثال، باستخدام نوعَي الإصدار release وdebug، ونوعَي المنتج demo وfull، سينشئ AGP صيغًا على النحو التالي:
demoReleasedemoDebugfullReleasefullDebug
الخطوات التالية
بعد أن تعرّفت على مفاهيم التصميم، ألقِ نظرة على بنية تصميم Android في مشروعك.