توصيات لبنية Android

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

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

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

البنية المتعدّدة الطبقات

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

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

تأكَّد من أنّ المكوّنات في طبقة واجهة المستخدم، مثل العناصر القابلة للإنشاء أو ViewModels، لا تتفاعل مباشرةً مع مصدر البيانات. تشمل أمثلة مصادر البيانات ما يلي:

  • قواعد البيانات وDataStore وSharedPreferences وواجهات برمجة تطبيقات Firebase
  • موفّرو الموقع الجغرافي في نظام تحديد المواقع العالمي (GPS)
  • مقدّمو بيانات البلوتوث
  • مقدّمو حالات اتصال الشبكة
استخدِم الروتينات المشتركة والتدفقات. استخدِم الروتينات المشتركة والتدفقات للتواصل بين الطبقات.

لمزيد من المعلومات حول أفضل الممارسات المتعلّقة بالكوروتينات، يُرجى الاطّلاع على أفضل الممارسات المتعلّقة بالكوروتينات في Android.

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

طبقة واجهة المستخدم

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

الاقتراح الوصف
اتّبِع تدفّق البيانات أحادي الاتجاه (UDF). اتّبِع مبادئ تدفّق البيانات أحادي الاتجاه (UDF)، حيث تعرض ViewModels حالة واجهة المستخدم باستخدام نمط المراقب وتتلقّى الإجراءات من واجهة المستخدم من خلال استدعاءات الطرق.
استخدِم AAC ViewModels إذا كانت مزاياها تنطبق على تطبيقك. استخدِم AAC ViewModels من أجل التعامل مع منطق النشاط التجاري واسترجاع بيانات التطبيق لعرض حالة واجهة المستخدم.

لمزيد من المعلومات عن أفضل الممارسات المتعلّقة بـ ViewModel، اطّلِع على اقتراحات حول بنية التطبيق.

لمزيد من المعلومات حول مزايا ViewModels، يمكنك الاطّلاع على ViewModel كعنصر الاحتفاظ بالحالة لمنطق النشاط التجاري.

استخدِم ميزة جمع حالات واجهة المستخدم المتوافقة مع مراحل النشاط. اجمع حالة واجهة المستخدم من واجهة المستخدم باستخدام أداة إنشاء الكوروتينات المناسبة المدرِكة لدورة الحياة، collectAsStateWithLifecycle.

مزيد من المعلومات حول collectAsStateWithLifecycle

لا ترسِل أحداثًا من ViewModel إلى واجهة المستخدم. معالجة الحدث على الفور في ViewModel وتعديل الحالة وفقًا لنتيجة معالجة الحدث لمزيد من المعلومات عن أحداث واجهة المستخدم، اطّلِع على مقالة التعامل مع أحداث ViewModel.
استخدِم تطبيقًا ذا نشاط واحد. استخدِم Navigation 3 للتنقّل بين الشاشات وإنشاء رابط لصفحة معيّنة في تطبيقك إذا كان تطبيقك يتضمّن أكثر من شاشة واحدة.
استخدِم Jetpack Compose. استخدِم Jetpack Compose لإنشاء تطبيقات جديدة للهواتف والأجهزة اللوحية والهواتف القابلة للطي وأجهزة Wear OS.

يوضّح المقتطف التالي كيفية جمع حالة واجهة المستخدم بطريقة تراعي مراحل النشاط:

  @Composable
  fun MyScreen(
      viewModel: MyViewModel = viewModel()
  ) {
      val uiState by viewModel.uiState.collectAsStateWithLifecycle()
  }

ViewModel

تتولّى ViewModels مسؤولية توفير حالة واجهة المستخدم والوصول إلى طبقة البيانات. في ما يلي بعض أفضل الممارسات المتعلّقة بـ ViewModels:

الاقتراح الوصف
يجب أن تكون ViewModels مستقلة عن دورة حياة Android. في ViewModels، لا تحتفظ بمَرجع لأي نوع ذي صلة بدورة الحياة. لا تمرِّر Activity أو Context أو Resources كعنصر تابع. إذا كان هناك شيء يحتاج إلى Context في ViewModel، عليك تقييم ما إذا كان ذلك في الطبقة الصحيحة بعناية.
استخدِم الروتينات المشتركة والتدفقات.

تتفاعل ViewModel مع طبقات البيانات أو النطاق باستخدام ما يلي:

  • مسارات Kotlin لتلقّي بيانات التطبيق
  • دوال suspend لتنفيذ الإجراءات باستخدام viewModelScope
استخدام ViewModels على مستوى الشاشة

لا تستخدِم ViewModels في أجزاء واجهة المستخدم القابلة لإعادة الاستخدام. يجب استخدام ViewModels في ما يلي:

  • عناصر قابلة للإنشاء على مستوى الشاشة،
  • الأنشطة/اللقطات في طرق العرض،
  • الوجهات أو الرسوم البيانية عند استخدام Jetpack Navigation
استخدِم عناصر الاحتفاظ بالحالة العادية في مكوّنات واجهة المستخدم القابلة لإعادة الاستخدام. استخدِم عناصر الاحتفاظ بالحالة العادية للتعامل مع التعقيد في مكوّنات واجهة المستخدم القابلة لإعادة الاستخدام. عند إجراء ذلك، يمكن نقل الحالة والتحكّم فيها خارجيًا.
لا تستخدِم AndroidViewModel. استخدِم فئة ViewModel، وليس AndroidViewModel. لا تستخدِم فئة Application في ViewModel. بدلاً من ذلك، يمكنك نقل التبعية إلى واجهة المستخدم أو طبقة البيانات.
عرض حالة واجهة المستخدم اجعل ViewModels تعرض البيانات لواجهة المستخدم من خلال سمة واحدة باسم uiState. إذا كانت واجهة المستخدم تعرض عدة أجزاء من البيانات غير المرتبطة ببعضها، يمكن للآلة الافتراضية عرض عدة سمات لحالة واجهة المستخدم.
  • تحويل uiState إلى StateFlow
  • أنشئ uiState باستخدام عامل التشغيل stateIn مع السياسة WhileSubscribed(5000) إذا كانت البيانات تأتي كتدفق بيانات من طبقات أخرى من التسلسل الهرمي. (اطّلِع على مثال الرمز البرمجي هذا).
  • في الحالات الأبسط التي لا تتضمّن مصادر بيانات واردة من طبقة البيانات، يمكن استخدام MutableStateFlow كـ StateFlow غير قابل للتغيير.
  • يمكنك اختيار ${Screen}UiState كفئة بيانات يمكن أن تحتوي على بيانات وأخطاء وإشارات تحميل. يمكن أن يكون هذا الصف أيضًا صفًا محكمًا إذا كانت الحالات المختلفة حصرية.

يوضّح المقتطف التالي كيفية عرض حالة واجهة المستخدم من ViewModel:

@HiltViewModel
class BookmarksViewModel @Inject constructor(
    newsRepository: NewsRepository
) : ViewModel() {

    val feedState: StateFlow<NewsFeedUiState> =
        newsRepository
            .getNewsResourcesStream()
            .mapToFeedState(savedNewsResourcesState)
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(5_000),
                initialValue = NewsFeedUiState.Loading
            )

    // ...
}

مراحل النشاط

اتّبِع أفضل الممارسات للتعامل مع دورة حياة النشاط:

الاقتراح الوصف
استخدِم التأثيرات التي تراعي مراحل النشاط في العناصر القابلة للإنشاء بدلاً من إلغاء معاودة الاتصال بدورة الحياة Activity.

لا تتجاوز طرق مراحل النشاط Activity، مثل onResume، لتنفيذ مهام متعلقة بواجهة المستخدم. بدلاً من ذلك، استخدِم LifecycleEffects في Compose أو نطاقات الروتينات المشتركة التي تراعي مراحل النشاط:

  • استخدِم LifecycleStartEffect لتنفيذ عمل متزامن عند بدء نشاطك وإيقافه.
  • استخدِم LifecycleResumeEffect لتنفيذ العمل المتزامن عند استئناف نشاطك وإيقافه مؤقتًا.
  • استخدِم repeatOnLifecycle لتنفيذ مهام غير متزامنة استجابةً لأحداث مراحل النشاط.
  • جمع البيانات غير المتزامنة من "عمليات سير العمل" باستخدام collectAsStateWithLifecycle

يوضّح المقتطف التالي كيفية تنفيذ العمليات في حالة Lifecycle معيّنة:

  @Composable
  fun LocationChangedEffect(
    locationManager: LocationManager,
    onLocationChanged: (Location) -> Unit
  ) {
    val currentOnLocationChanged by rememberUpdatedState(onLocationChanged)

    LifecycleStartEffect(locationManager) {
        val listener = LocationListener { newLocation ->
            currentOnLocationChanged(newLocation)
        }

        try {
            locationManager.requestLocationUpdates(
                LocationManager.GPS_PROVIDER,
                1000L,
                1f,
                listener,
            )
        } catch (e: SecurityException) {
            // TODO: Handle missing permissions
        }

        onStopOrDispose {
            locationManager.removeUpdates(listener)
        }
    }
  }

التعامل مع العناصر التابعة

اتّبِع أفضل الممارسات عند إدارة التبعيات بين المكوّنات:

الاقتراح الوصف
استخدِم إدخال التبعية. استخدِم أفضل ممارسات إدخال التبعية، وخاصةً إدخال الدالة الإنشائية عند الإمكان.
استخدِم نطاقًا خاصًا بمكوّن معيّن عند الضرورة. يمكنك تحديد نطاق حاوية التبعية عندما يحتوي النوع على بيانات قابلة للتغيير يجب مشاركتها أو عندما يكون النوع مكلفًا في عملية التهيئة ويُستخدم على نطاق واسع في التطبيق.
استخدِم Hilt. استخدِم Hilt أو إدخال التبعية يدويًا في التطبيقات البسيطة. استخدِم Hilt إذا كان مشروعك معقّدًا بما يكفي، مثلاً إذا كان يتضمّن أيًا مما يلي:
  • شاشات متعدّدة باستخدام ViewModels
  • استخدام WorkManager
  • تتضمّن ViewModels محدّدة النطاق في سجلّ التنقّل السابق

الاختبار

في ما يلي بعض أفضل الممارسات المتعلّقة بالاختبار:

الاقتراح الوصف
معرفة ما يجب اختباره:

ما لم يكن المشروع بسيطًا مثل تطبيق "hello world"، اختبِره. يجب تضمين ما يلي على الأقل:

  • اختبارات الوحدة لـ ViewModels، بما في ذلك Flows
  • اختبارات الوحدات لكيانات طبقة البيانات، أي المستودعات ومصادر البيانات
  • اختبارات التنقّل في واجهة المستخدم التي تكون مفيدة كاختبارات الانحدار في عملية التكامل المستمر
يُفضّل استخدام عمليات محاكاة حقيقية بدلاً من عمليات محاكاة وهمية. لمزيد من المعلومات حول استخدام العناصر الوهمية، راجِع استخدام العناصر البديلة في Android.
اختبار StateFlows عند اختبار StateFlow، اتّبِع الخطوات التالية:

لمزيد من المعلومات، اطّلِع على ما يجب اختباره في Android واختبار تصميم Compose.

النماذج

اتّبِع أفضل الممارسات التالية عند تطوير النماذج في تطبيقاتك:

الاقتراح الوصف
أنشئ نموذجًا لكل طبقة في التطبيقات المعقّدة.

في التطبيقات المعقّدة، أنشِئ نماذج جديدة في طبقات أو مكوّنات مختلفة عندما يكون ذلك منطقيًا. في ما يلي أمثلة:

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

اصطلاحات التسمية

عند تسمية قاعدة الرموز البرمجية، يجب أن تكون على دراية بأفضل الممارسات التالية:

الاقتراح الوصف
طرق التسمية
اختياري
استخدِم عبارات فعلية لتسمية الطرق، مثل makePayment().
تسمية الخصائص
اختياري
استخدِم عبارات اسمية لتسمية السمات، مثل inProgressTopicSelection.
تسمية مصادر البيانات
اختياري
عندما يعرض صف دفق Flow أو أي دفق آخر، يكون اصطلاح التسمية هو get{model}Stream. على سبيل المثال، getAuthorStream(): Flow<Author>. إذا كانت الدالة تعرض قائمة بالنماذج، استخدِم اسم النموذج بصيغة الجمع: getAuthorsStream(): Flow<List<Author>>.
تسمية عمليات تنفيذ الواجهات
اختياري
استخدِم أسماءً معبّرة لعمليات تنفيذ الواجهات. استخدِم Default كبادئة إذا لم يكن بالإمكان العثور على اسم أفضل. على سبيل المثال، بالنسبة إلى واجهة NewsRepository، قد يكون لديك OfflineFirstNewsRepository أو InMemoryNewsRepository. إذا لم تتمكّن من العثور على اسم مناسب، استخدِم DefaultNewsRepository. ابدأ عمليات التنفيذ الوهمية بالبادئة Fake، كما في FakeAuthorsRepository.

مراجع إضافية

لمزيد من المعلومات حول بنية Android، يُرجى الاطّلاع على المراجع الإضافية التالية:

المستندات

مشاهدة المحتوى