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

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

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

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

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

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

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

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

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

مزيد من أفضل الممارسات بخصوص الكوروتينات هنا

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

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

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

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

اطّلِع على المزيد من أفضل ممارسات View Model هنا.

اطّلِع على مزايا ViewModels هنا.

استخدام مجموعة حالة واجهة المستخدم الواعية لمراحل النشاط
يُنصح به بشدة
اجمع حالة واجهة المستخدم من واجهة المستخدم باستخدام أداة إنشاء الكوروتينات الواعية لمراحل النشاط: repeatOnLifecycle في نظام العرض وcollectAsStateWithLifecycle في Jetpack Compose.

الاطّلاع على مزيد من المعلومات عن repeatOnLifecycle

يمكنك الاطّلاع على مزيد من المعلومات عن collectAsStateWithLifecycle.

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

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

المشاهدات

class MyFragment : Fragment() {

    private val viewModel: MyViewModel by viewModel()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect {
                    // Process item
                }
            }
        }
    }
}

إنشاء

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

عرض النموذج

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

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

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

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

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

  • عناصر قابلة للإنشاء على مستوى الشاشة
  • الأنشطة/الأجزاء في المشاهدات،
  • الوجهات أو الرسوم البيانية عند استخدام ميزة التنقل في Jetpack.
استخدام فئات صاحب الحالة العادية في مكونات واجهة المستخدم القابلة لإعادة الاستخدام.
يُنصح به بشدة
استخدِم فئات صاحب الحالة العادية للتعامل مع التعقيدات في مكونات واجهة المستخدم القابلة لإعادة الاستخدام. ومن خلال القيام بذلك، يمكن رفع الدولة والتحكم فيها خارجيًا.
لا تستخدِم 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
            )

    // ...
}

مراحل النشاط

في ما يلي بعض أفضل الممارسات للتعامل مع نظام التشغيل Android مراحل النشاط:

مقترَح الوصف
لا تتجاهل طرق مراحل النشاط في الأنشطة أو الأجزاء.
يُنصح به بشدة
لا تتجاهل طُرق مراحل النشاط، مثل onResume في الأنشطة أو الأجزاء. يمكنك استخدام LifecycleObserver بدلاً من ذلك. إذا كان التطبيق بحاجة إلى العمل عند بلوغ مراحل النشاط Lifecycle.State معيّنة، استخدِم واجهة برمجة التطبيقات repeatOnLifecycle.

يوضح المقتطف التالي كيفية إجراء عمليات بناءً على حالة مراحل النشاط:

المشاهدات

class MyFragment: Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
            override fun onResume(owner: LifecycleOwner) {
                // ...
            }
            override fun onPause(owner: LifecycleOwner) {
                // ...
            }
        }
    }
}

إنشاء

@Composable
fun MyApp() {

    val lifecycleOwner = LocalLifecycleOwner.current
    DisposableEffect(lifecycleOwner, ...) {
        val lifecycleObserver = object : DefaultLifecycleObserver {
            override fun onStop(owner: LifecycleOwner) {
                // ...
            }
        }

        lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
        onDispose {
            lifecycleOwner.lifecycle.removeObserver(lifecycleObserver)
        }
    }
}

التعامل مع التبعيات

هناك العديد من أفضل الممارسات التي يجب ملاحظتها عند إدارة التبعيات بين المكونات:

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

الاختبار

في ما يلي بعض أفضل الممارسات لإجراء الاختبار:

مقترَح الوصف
اطّلِع على ما يجب اختباره.
يُنصح به بشدة

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

  • نماذج Viewview لاختبار الوحدة، بما في ذلك المسارات
  • كيانات طبقة بيانات اختبار الوحدة أي المستودعات ومصادر البيانات.
  • هي اختبارات للتنقل في واجهة المستخدم وهي مفيدة كاختبارات الانحدار في CI.
تفضيل الزائفة على النماذج التجريبية
يُنصح به بشدة
يمكنك الاطّلاع على مزيد من المعلومات في استخدام أدوات الاختبار المزدوجة في مستندات Android.
اختبار StateFlows
يُنصح به بشدة
عند اختبار StateFlow:

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

نماذج

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

مقترَح الوصف
إنشاء نموذج لكل طبقة في التطبيقات المعقّدة
إجراءات ننصح بها

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

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

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

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

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