اقتراحات بشأن بنية Android (طرق العرض)

المفاهيم والتنفيذ في Jetpack Compose

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

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

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

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

اقتراح

الوصف

اتّبِع تدفّق البيانات أحادي الاتجاه (UDF).

يُنصح به بشدة

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

استخدِم AAC ViewModels إذا كانت مزاياها تنطبق على تطبيقك.

يُنصح به بشدة

استخدِم AAC ViewModels من أجل التعامل مع منطق النشاط التجاري واسترجاع بيانات التطبيق لعرض حالة واجهة المستخدم.

يمكنك الاطّلاع على المزيد من أفضل الممارسات المتعلّقة بفئة ViewModel هنا.

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

استخدِم ميزة جمع حالات واجهة المستخدم المتوافقة مع مراحل النشاط.

يُنصح به بشدة

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

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

لا ترسِل أحداثًا من ViewModel إلى واجهة المستخدم.

يُنصح به بشدة

معالجة الحدث على الفور في ViewModel وتعديل الحالة وفقًا لنتيجة معالجة الحدث مزيد من المعلومات عن أحداث واجهة المستخدم هنا

استخدِم تطبيقًا ذا نشاط واحد.

ننصح به

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

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

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
                }
            }
        }
    }
}

ViewModel

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

اقتراح

الوصف

يجب أن تكون ViewModels مستقلة عن مراحل نشاط Android.

يُنصح به بشدة

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

استخدِم الروتينات المشتركة والتدفقات.

يُنصح به بشدة

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

  • عمليات Kotlin لتلقّي بيانات التطبيق
  • تتضمّن suspend دوالاً لتنفيذ الإجراءات باستخدام viewModelScope.

استخدِم ViewModels على مستوى الشاشة.

يُنصح به بشدة

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

  • الأنشطة/اللقطات في طرق العرض
  • الوجهات أو الرسوم البيانية عند استخدام Jetpack Navigation

لا تستخدِم AndroidViewModel.

يُنصح به بشدة

استخدِم فئة ViewModel، وليس AndroidViewModel. يجب عدم استخدام الفئة Application في ViewModel. بدلاً من ذلك، يمكنك نقل التبعية إلى واجهة المستخدم أو طبقة البيانات.

عرض حالة واجهة المستخدم

ننصح به

يجب أن تعرض ViewModels البيانات لواجهة المستخدم من خلال سمة واحدة تُسمى uiState. إذا كانت واجهة المستخدم تعرض عدة أجزاء من البيانات غير ذات صلة، يمكن أن تعرض ViewModel سمات متعددة لحالة واجهة المستخدم.

  • عليك تحويل 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.

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

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) {
                // ...
            }
        }
    }
}