أنماط التقسيم الشائعة

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

مبدأ التماسك العالي والاقتران المنخفض

إحدى طرق تحديد خصائص قاعدة الرموز البرمجية النموذجية هي استخدام سمتَي الاقتران والتماسك. يقيس الربط درجة اعتماد الوحدات النمطية على بعضها البعض. في هذا السياق، تقيس درجة التماسك مدى ارتباط عناصر الوحدة النمطية الواحدة من الناحية الوظيفية. كقاعدة عامة، يجب السعي إلى تحقيق ما يلي:

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

أنواع الوحدات

تعتمد طريقة تنظيم الوحدات بشكل أساسي على بنية تطبيقك. في ما يلي بعض الأنواع الشائعة من الوحدات التي يمكنك إضافتها إلى تطبيقك مع اتّباع بنية التطبيق المقترَحة.

وحدات البيانات

يحتوي نموذج البيانات عادةً على مستودع ومصادر بيانات وفئات نماذج. المسؤوليات الأساسية الثلاث لوحدة البيانات هي:

  1. تغليف جميع البيانات ومنطق النشاط التجاري الخاص بنطاق معيّن: يجب أن يكون كل وحدة بيانات مسؤولة عن معالجة البيانات التي تمثّل نطاقًا معيّنًا. يمكنه التعامل مع العديد من أنواع البيانات طالما أنّها مرتبطة.
  2. عرض المستودع كواجهة برمجة تطبيقات خارجية: يجب أن تكون واجهة برمجة التطبيقات العامة لوحدة البيانات مستودعًا لأنّها مسؤولة عن عرض البيانات لبقية التطبيق.
  3. إخفاء جميع تفاصيل التنفيذ ومصادر البيانات عن الخارج: يجب أن تكون مصادر البيانات متاحة فقط للمستودعات من الوحدة النمطية نفسها. وتبقى مخفية عن الخارج. يمكنك فرض ذلك باستخدام الكلمة الرئيسية الخاصة بمستوى الظهور private أو internal في Kotlin.
الشكل 1. وحدات البيانات النموذجية ومحتواها

وحدات الميزات

الميزة هي جزء مستقل من وظائف التطبيق يتوافق عادةً مع شاشة أو سلسلة من الشاشات ذات الصلة الوثيقة، مثل عملية التسجيل أو الدفع. إذا كان تطبيقك يتضمّن شريط تنقّل في أسفل الشاشة، من المحتمل أن تكون كل وجهة ميزة.

الشكل 2. يمكن تعريف كل علامة تبويب في هذا التطبيق كميزة.

ترتبط الميزات بالشاشات أو وجهات معيّنة في تطبيقك، لذا من المحتمل أن يكون لها واجهة مستخدم مرتبطة بها وViewModel للتعامل مع منطقها وحالتها. لا يجب أن تقتصر الميزة الواحدة على طريقة عرض واحدة أو وجهة تنقّل واحدة. تعتمد وحدات الميزات على وحدات البيانات.

الشكل 3. نماذج لوحدات الميزات ومحتواها

وحدات التطبيق

وحدات التطبيق هي نقطة دخول إلى التطبيق. وتعتمد على وحدات الميزات وتوفّر عادةً التنقّل الأساسي. يمكن تجميع وحدة تطبيق واحدة في عدد من البرامج الثنائية المختلفة بفضل تنويعات التصميم.

الشكل 4 الرسم البياني للمهام التابعة لوحدات صيغة المنتج *التجريبية* و *الكاملة*

إذا كان تطبيقك يستهدف أنواعًا متعددة من الأجهزة، مثل Android Auto أو Wear أو TV، عليك تحديد وحدة تطبيق لكل نوع. يساعد ذلك في فصل التبعيات الخاصة بالنظام الأساسي.

الشكل 5. الرسم البياني للمهام التابعة في تطبيق Android Auto

الوحدات الشائعة

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

  • وحدة واجهة المستخدم: إذا كنت تستخدم عناصر واجهة مستخدم مخصّصة أو علامات تجارية مفصّلة في تطبيقك، ننصحك بتضمين مجموعة الأدوات في وحدة لكي تتمكّن جميع الميزات من إعادة استخدامها. ويمكن أن يساعد ذلك في الحفاظ على اتساق واجهة المستخدم في جميع الميزات المختلفة. على سبيل المثال، إذا كان تصميمك موحّدًا، يمكنك تجنُّب إعادة تصميم مؤلمة عند تغيير العلامة التجارية.
  • وحدة "إحصاءات Google": غالبًا ما يتم تحديد التتبُّع حسب متطلبات النشاط التجاري مع مراعاة بنية البرنامج بشكل محدود. ويتم غالبًا استخدام أدوات التتبُّع الإحصائية في العديد من المكوّنات غير المرتبطة. إذا كان هذا هو الحال بالنسبة إليك، قد يكون من المفيد توفير وحدة إحصاءات مخصّصة.
  • وحدة الشبكة: عندما تتطلّب العديد من الوحدات اتصالاً بالشبكة، يمكنك استخدام وحدة مخصّصة لتوفير برنامج http. ويكون هذا الإجراء مفيدًا بشكل خاص عندما يحتاج العميل إلى إعدادات مخصّصة.
  • وحدة الأدوات المساعدة: الأدوات المساعدة، المعروفة أيضًا باسم أدوات المساعدة، هي عادةً أجزاء صغيرة من الرمز البرمجي يتم إعادة استخدامها في جميع أنحاء التطبيق. تشمل أمثلة الأدوات المساعدة ما يلي: أدوات مساعدة للاختبار، ودالة لتنسيق العملة، وأداة للتحقّق من صحة عنوان البريد الإلكتروني، أو عامل تشغيل مخصّص.

وحدات الاختبار

وحدات الاختبار هي وحدات Android تُستخدَم لأغراض الاختبار فقط. تحتوي الوحدات على رمز الاختبار وموارد الاختبار وتبعيات الاختبار المطلوبة فقط لتنفيذ الاختبارات، ولا تكون مطلوبة أثناء وقت تشغيل التطبيق. يتم إنشاء وحدات الاختبار لفصل الرمز البرمجي الخاص بالاختبار عن التطبيق الرئيسي، ما يسهّل إدارة رمز الوحدة وصيانته.

حالات استخدام وحدات الاختبار

تعرض الأمثلة التالية حالات يمكن أن يكون فيها تنفيذ الوحدات الاختبارية مفيدًا بشكل خاص:

  • رمز الاختبار المشترَك: إذا كان لديك وحدات متعدّدة في مشروعك وكان بعض رموز الاختبار قابلاً للتطبيق على أكثر من وحدة، يمكنك إنشاء وحدة اختبار لمشاركة الرمز. يمكن أن يساعد ذلك في الحد من التكرار وتسهيل صيانة رمز الاختبار. يمكن أن تتضمّن التعليمات البرمجية للاختبار المشترَكة فئات أو دوال مساعدة، مثل عمليات التأكيد أو المطابقة المخصّصة، بالإضافة إلى بيانات الاختبار، مثل الردود بتنسيق JSON المحاكية.

  • إعدادات تصميم أكثر وضوحًا: تتيح لك وحدات الاختبار الحصول على إعدادات تصميم أكثر وضوحًا، إذ يمكن أن يكون لكل منها ملف build.gradle خاص بها. ليس عليك إضافة إعدادات لا صلة لها إلا بالاختبارات إلى ملف build.gradle الخاص بوحدة تطبيقك.

  • اختبارات الدمج: يمكن استخدام وحدات الاختبار لتخزين اختبارات الدمج التي تُستخدَم لاختبار التفاعلات بين أجزاء مختلفة من تطبيقك، بما في ذلك واجهة المستخدم ومنطق النشاط التجاري وطلبات الشبكة واستعلامات قاعدة البيانات.

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

الشكل 6. يمكن استخدام وحدات الاختبار لعزل الوحدات التي تعتمد على بعضها البعض.

التواصل بين الوحدات

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

الشكل 7. لا يمكن إجراء اتصال مباشر ثنائي الاتجاه بين الوحدات بسبب التبعيات الدورية. من الضروري توفُّر وحدة وسيطة لتنسيق تدفق البيانات بين وحدتَين مستقلتَين أخريَين.

للتغلّب على هذه المشكلة، يمكنك استخدام وحدة ثالثة للتوسّط بين وحدتَين أخريَين. يمكن لوحدة الوسيط الاستماع إلى الرسائل الواردة من كلتا الوحدتين وإعادة توجيهها حسب الحاجة. في تطبيقنا النموذجي، يجب أن تعرف شاشة الدفع الكتاب الذي سيتم شراؤه، حتى إذا كان الحدث قد بدأ في شاشة منفصلة تشكّل جزءًا من ميزة مختلفة. في هذه الحالة، يكون الوسيط هو الوحدة التي تملك الرسم البياني للتنقّل (عادةً ما تكون وحدة تطبيق). في المثال، نستخدم التنقّل لتمرير البيانات من ميزة الصفحة الرئيسية إلى ميزة الدفع باستخدام مكوّن Navigation.

navController.navigate("checkout/$bookId")

يتلقّى وجهة الدفع معرّف كتاب كمعلَمة تستخدمها لجلب معلومات حول الكتاب. يمكنك استخدام معالج الحالة المحفوظة لاسترداد وسيطات التنقّل داخل ViewModel لإحدى ميزات الوجهة.

class CheckoutViewModel(savedStateHandle: SavedStateHandle, ) : ViewModel() {

   val uiState: StateFlow<CheckoutUiState> =
      savedStateHandle.getStateFlow<String>("bookId", "").map { bookId ->
          // produce UI state calling bookRepository.getBook(bookId)
      }
      
}

يجب عدم تمرير الكائنات كوسيطات تنقّل. بدلاً من ذلك، استخدِم معرّفات بسيطة يمكن أن تستخدمها الميزات للوصول إلى الموارد المطلوبة وتحميلها من طبقة البيانات. بهذه الطريقة، يمكنك الحفاظ على مستوى اقتران منخفض وعدم انتهاك مبدأ المصدر الواحد.

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

الشكل 8. وحدتا ميزات تعتمدان على وحدة بيانات مشترَكة

انعكاس التبعية

يحدث عكس التبعية عندما تنظّم الرمز البرمجي بطريقة تجعل التجريد منفصلاً عن التنفيذ الملموس.

  • التجريد: هو عقد يحدّد كيفية تفاعل المكوّنات أو الوحدات في تطبيقك مع بعضها البعض. تحدّد وحدات التجريد واجهة برمجة التطبيقات لنظامك، وتحتوي على واجهات ونماذج.
  • التنفيذ الملموس: الوحدات التي تعتمد على وحدة التجريد وتنفّذ سلوك التجريد

يجب أن تعتمد الوحدات التي تستند إلى السلوك المحدّد في وحدة التجريد على التجريد نفسه فقط، وليس على عمليات التنفيذ المحدّدة.

الشكل 9 بدلاً من أن تعتمد الوحدات ذات المستوى العالي على الوحدات ذات المستوى المنخفض بشكل مباشر، تعتمد الوحدات ذات المستوى العالي ووحدات التنفيذ على وحدة التجريد.

مثال

لنفترض أنّ لديك وحدة ميزات تحتاج إلى قاعدة بيانات لكي تعمل. لا يهتم نموذج الميزة بطريقة تنفيذ قاعدة البيانات، سواء كانت قاعدة بيانات Room محلية أو مثيلاً بعيدًا من Firestore. ويحتاج فقط إلى تخزين بيانات التطبيق وقراءتها.

ولتحقيق ذلك، يعتمد وحدة الميزات على وحدة التجريد بدلاً من الاعتماد على تنفيذ محدّد لقاعدة البيانات. يحدّد هذا التجريد واجهة برمجة التطبيقات لقاعدة البيانات الخاصة بالتطبيق. بمعنى آخر، يحدّد هذا النظام قواعد التفاعل مع قاعدة البيانات. يتيح ذلك لوحدة الميزات استخدام أي قاعدة بيانات بدون الحاجة إلى معرفة تفاصيل التنفيذ الأساسية.

تقدّم وحدة التنفيذ الملموس التنفيذ الفعلي لواجهات برمجة التطبيقات المحدّدة في وحدة التجريد. ولإجراء ذلك، يعتمد نموذج التنفيذ أيضًا على نموذج التجريد.

تضمين التبعية

قد تتساءل الآن عن كيفية ربط وحدة الميزات بوحدة التنفيذ. الإجابة هي تضمين التبعية. لا ينشئ وحدة الميزات مثيلاً لقاعدة البيانات المطلوبة بشكل مباشر. بدلاً من ذلك، يحدّد هذا الملف التبعيات التي يحتاجها. بعد ذلك، يتم توفير هذه التبعيات خارجيًا، وعادةً في وحدة تطبيق.

releaseImplementation(project(":database:impl:firestore"))

debugImplementation(project(":database:impl:room"))

androidTestImplementation(project(":database:impl:mock"))

المزايا

في ما يلي مزايا فصل واجهات برمجة التطبيقات عن عمليات التنفيذ:

  • إمكانية التبديل: من خلال الفصل الواضح بين واجهة برمجة التطبيقات ووحدات التنفيذ، يمكنك تطوير عمليات تنفيذ متعددة لواجهة برمجة التطبيقات نفسها، والتبديل بينها بدون تغيير الرمز الذي يستخدم واجهة برمجة التطبيقات. وقد يكون ذلك مفيدًا بشكل خاص في السيناريوهات التي تريد فيها توفير إمكانات أو سلوكيات مختلفة في سياقات مختلفة. على سبيل المثال، تنفيذ وهمي للاختبار مقابل تنفيذ حقيقي للإنتاج.
  • الفصل: يعني الفصل أنّ الوحدات التي تستخدم التجريدات لا تعتمد على أي تكنولوجيا محدّدة. إذا اخترت تغيير قاعدة البيانات من Room إلى Firestore في وقت لاحق، سيكون ذلك أسهل لأنّ التغييرات ستحدث فقط في الوحدة النمطية المحدّدة التي تنفّذ المهمة (وحدة التنفيذ) ولن تؤثّر في الوحدات النمطية الأخرى التي تستخدم واجهة برمجة التطبيقات لقاعدة البيانات.
  • إمكانية الاختبار: يمكن أن يؤدي فصل واجهات برمجة التطبيقات عن عمليات التنفيذ إلى تسهيل الاختبار بشكل كبير. يمكنك كتابة حالات اختبار مقابل عقود واجهة برمجة التطبيقات. يمكنك أيضًا استخدام عمليات تنفيذ مختلفة لاختبار سيناريوهات وحالات حدودية متنوعة، بما في ذلك عمليات التنفيذ التجريبية.
  • تحسين أداء عملية الإنشاء: عند فصل واجهة برمجة التطبيقات وتنفيذها في وحدات مختلفة، لن تؤدي التغييرات في وحدة التنفيذ إلى إجبار نظام الإنشاء على إعادة تجميع الوحدات التي تعتمد على وحدة واجهة برمجة التطبيقات. ويؤدي ذلك إلى تقليل أوقات الإنشاء وزيادة الإنتاجية، خاصةً في المشاريع الكبيرة التي يمكن أن تكون فيها أوقات الإنشاء كبيرة.

الحالات التي يجب فيها الفصل

من المفيد فصل واجهات برمجة التطبيقات عن عمليات التنفيذ في الحالات التالية:

  • إمكانات متنوّعة: إذا كان بإمكانك تنفيذ أجزاء من نظامك بطرق متعدّدة، تتيح واجهة برمجة التطبيقات الواضحة إمكانية تبادل عمليات التنفيذ المختلفة. على سبيل المثال، قد يكون لديك نظام عرض يستخدم OpenGL أو Vulkan، أو نظام فوترة يعمل مع Play أو واجهة برمجة التطبيقات الداخلية لنظام الفوترة.
  • تطبيقات متعددة: إذا كنت بصدد تطوير تطبيقات متعددة تتضمّن إمكانات مشتركة لأنظمة أساسية مختلفة، يمكنك تحديد واجهات برمجة تطبيقات مشتركة وتطوير عمليات تنفيذ محدّدة لكل نظام أساسي.
  • الفرق المستقلة: يتيح الفصل بين الوحدات لمطوّرين أو فِرق مختلفة العمل على أجزاء مختلفة من قاعدة الرموز البرمجية في الوقت نفسه. على المطوّرين التركيز على فهم عقود واجهة برمجة التطبيقات واستخدامها بشكل صحيح. ولا يحتاجون إلى القلق بشأن تفاصيل تنفيذ الوحدات الأخرى.
  • قاعدة رموز برمجية كبيرة: عندما تكون قاعدة الرموز البرمجية كبيرة أو معقّدة، يؤدي فصل واجهة برمجة التطبيقات عن التنفيذ إلى تسهيل إدارة الرموز البرمجية. يتيح لك ذلك تقسيم قاعدة الرموز إلى وحدات أكثر تفصيلاً وقابلة للفهم والصيانة.

كيفية إعدادها

لتنفيذ مبدأ عكس التبعية، اتّبِع الخطوات التالية:

  1. إنشاء وحدة تجريد: يجب أن تحتوي هذه الوحدة على واجهات برمجة تطبيقات (واجهات ونماذج) تحدّد سلوك الميزة.
  2. إنشاء وحدات التنفيذ: يجب أن تعتمد وحدات التنفيذ على وحدة واجهة برمجة التطبيقات وأن تنفّذ سلوك التجريد.
    بدلاً من أن تعتمد الوحدات ذات المستوى العالي على الوحدات ذات المستوى المنخفض بشكل مباشر، تعتمد الوحدات ذات المستوى العالي ووحدات التنفيذ على وحدة التجريد.
    الشكل 10. تعتمد وحدات التنفيذ على وحدة التجريد.
  3. جعل الوحدات ذات المستوى العالي تعتمد على وحدات التجريد: بدلاً من الاعتماد مباشرةً على تنفيذ معيّن، اجعل الوحدات تعتمد على وحدات التجريد. لا تحتاج الوحدات ذات المستوى العالي إلى معرفة تفاصيل التنفيذ، بل تحتاج فقط إلى العقد (واجهة برمجة التطبيقات).
    تعتمد الوحدات ذات المستوى العالي على التجريدات، وليس على التنفيذ.
    الشكل 11. تعتمد الوحدات ذات المستوى العالي على التجريدات، وليس على التنفيذ.
  4. توفير وحدة التنفيذ: أخيرًا، عليك توفير التنفيذ الفعلي لعمليات التبعية. تعتمد طريقة التنفيذ المحدّدة على إعداد مشروعك، ولكن عادةً ما يكون وحدة التطبيق مكانًا مناسبًا لتنفيذ ذلك. لتوفير التنفيذ، حدِّده كعنصر تابع لمتغير الإصدار أو مجموعة مصادر الاختبار المحدّدة.
    يوفر نموذج التطبيق التنفيذ الفعلي.
    الشكل 12 يوفر نموذج التطبيق التنفيذ الفعلي.

أفضل الممارسات العامة

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

الحفاظ على اتساق الإعدادات

تتسبّب كل وحدة في زيادة الحمل الزائد في عملية الإعداد. إذا وصل عدد الوحدات إلى حد معيّن، سيصبح من الصعب إدارة الإعدادات المتسقة. على سبيل المثال، من المهم أن تستخدم الوحدات النمطية إصدارات متوافقة من التبعيات. إذا كنت بحاجة إلى تعديل عدد كبير من الوحدات النمطية لمجرّد رفع مستوى إصدار إحدى التبعيات، لن يكون ذلك مجهودًا فحسب، بل سيؤدي أيضًا إلى حدوث أخطاء محتملة. لحلّ هذه المشكلة، يمكنك استخدام إحدى أدوات Gradle لتنفيذ ما يلي:

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

الحدّ من التعرّض قدر الإمكان

يجب أن تكون الواجهة العامة للوحدة النمطية في الحد الأدنى وأن تعرض فقط العناصر الأساسية. ويجب ألا يتم تسريب أي تفاصيل عن التنفيذ إلى الخارج. يجب أن يكون النطاق محدودًا بأصغر قدر ممكن. استخدِم نطاق إذن الوصول private أو internal في Kotlin لجعل تعريفات الوحدات خاصة بالوحدة. عند تعريف التبعيات في الوحدة، يُفضَّل استخدام implementation بدلاً من api. ويعرض الأخير التبعيات المتعدية لمستهلكي الوحدة. قد يؤدي استخدام التنفيذ إلى تحسين وقت الإنشاء لأنّه يقلّل عدد الوحدات التي يجب إعادة إنشائها.

تفضيل وحدات Kotlin وJava

هناك ثلاثة أنواع أساسية من الوحدات التي يتيحها &quot;استوديو Android&quot;:

  • وحدات التطبيق هي نقطة دخول إلى تطبيقك. ويمكن أن تحتوي على الرمز المصدري والموارد والأصول وAndroidManifest.xml. يكون الناتج من وحدة تطبيق عبارة عن حزمة Android App Bundle (AAB) أو حزمة تطبيق Android (APK).
  • تحتوي وحدات المكتبة على المحتوى نفسه المتوفّر في وحدات التطبيق. وتستخدمها وحدات Android النمطية الأخرى كعنصر تابع. إنّ ناتج وحدة المكتبة هو &quot;أرشيف Android&quot; (AAR) الذي يتطابق هيكليًا مع وحدات التطبيق، ولكن يتم تجميعه في ملف &quot;أرشيف Android&quot; (AAR) يمكن أن تستخدمه الوحدات الأخرى لاحقًا كعنصر تابع. يتيح لك نموذج المكتبة تضمين وإعادة استخدام المنطق والموارد نفسها في العديد من وحدات التطبيق.
  • لا تحتوي مكتبات Kotlin وJava على أي موارد أو مواد عرض أو ملفات بيان لنظام Android.

بما أنّ وحدات Android تتضمّن تكاليف إضافية، يُفضّل استخدام نوع Kotlin أو Java قدر الإمكان.