تعرض هذه الصفحة العديد من أفضل الممارسات والاقتراحات المتعلّقة بالبنية. استخدِمها لتحسين جودة تطبيقك وموثوقيته وقابليته للتوسّع. كما تسهّل عليك هذه الأدوات صيانة تطبيقك واختباره.
تم تجميع أفضل الممارسات أدناه حسب الموضوع. لكل منها أولوية تعكس مدى أهمية الاقتراح، وفي ما يلي قائمة الأولويات:
- ننصح بشدة باتّباع هذه الممارسة: ننصحك بتطبيق هذه الممارسة ما لم تتعارض بشكل أساسي مع أسلوبك.
- يُنصح به: من المرجّح أن تؤدي هذه الممارسة إلى تحسين تطبيقك.
- اختياري: يمكن أن تساعد هذه الممارسة في تحسين تطبيقك في ظروف معيّنة.
البنية المتعدّدة الطبقات
تفضّل البنية المتعدّدة الطبقات التي ننصح بها فصل الاهتمامات. تستند واجهة المستخدم إلى نماذج البيانات، وتلتزم بمبدأ المصدر الواحد الموثوق، وتتّبع مبادئ تدفّق البيانات أحادي الاتجاه. في ما يلي بعض أفضل الممارسات المتعلّقة بالتصميم المعماري ذي الطبقات:
| الاقتراح | الوصف |
|---|---|
| استخدِم طبقة بيانات محدّدة بوضوح.
يُنصح بشدة |
تعرض طبقة البيانات بيانات التطبيق لبقية التطبيق، وتحتوي على الغالبية العظمى من منطق النشاط التجاري لتطبيقك.
|
| استخدِم طبقة واجهة مستخدم محدّدة بوضوح.
يُنصح بشدة |
تعرض طبقة واجهة المستخدِم بيانات التطبيق على الشاشة وتشكّل نقطة التفاعل الأساسية للمستخدم. Jetpack Compose هي مجموعة الأدوات الحديثة المقترَحة لإنشاء واجهة مستخدم تطبيقك.
|
| عرض بيانات التطبيق من طبقة البيانات باستخدام مستودع
يُنصح بشدة |
تأكَّد من أنّ المكوّنات في طبقة واجهة المستخدم، مثل العناصر القابلة للإنشاء أو ViewModels، لا تتفاعل مباشرةً مع مصدر البيانات. تشمل أمثلة مصادر البيانات ما يلي:
|
| استخدِم الروتينات المشتركة والتدفقات.
يُنصح بشدة |
استخدِم الروتينات المشتركة والتدفقات للتواصل بين الطبقات.
لمزيد من المعلومات حول أفضل الممارسات المتعلّقة بالكوروتينات، يُرجى الاطّلاع على أفضل الممارسات المتعلّقة بالكوروتينات في Android. |
| استخدِم طبقة نطاق.
مقترَحة في التطبيقات الكبيرة |
استخدِم طبقة النطاق مع حالات الاستخدام إذا كنت بحاجة إلى إعادة استخدام منطق النشاط التجاري الذي يتفاعل مع طبقة البيانات في عدة ViewModels، أو إذا كنت تريد تبسيط تعقيد منطق النشاط التجاري في ViewModel معيّن. |
طبقة واجهة المستخدم
يتمثل دور طبقة واجهة المستخدم في عرض بيانات التطبيق على الشاشة والعمل كنقطة أساسية لتفاعل المستخدم. في ما يلي بعض أفضل الممارسات المتعلّقة بطبقة واجهة المستخدم:
| الاقتراح | الوصف |
|---|---|
| اتّبِع تدفّق البيانات أحادي الاتجاه (UDF).
يُنصح بشدة |
اتّبِع مبادئ تدفّق البيانات أحادي الاتجاه (UDF)، حيث تعرض ViewModels حالة واجهة المستخدم باستخدام نمط المراقب وتتلقّى الإجراءات من واجهة المستخدم من خلال استدعاءات الطرق. |
| استخدِم AAC ViewModels إذا كانت مزاياها تنطبق على تطبيقك.
يُنصح بشدة |
استخدِم AAC ViewModels من أجل التعامل مع منطق النشاط التجاري، واسترجاع بيانات التطبيق لعرض حالة واجهة المستخدم على واجهة المستخدم.
لمزيد من المعلومات عن أفضل الممارسات المتعلّقة بـ ViewModel، راجِع اقتراحات حول بنية التطبيق. لمزيد من المعلومات حول مزايا ViewModels، يُرجى الاطّلاع على مقالة ViewModel كعنصر الاحتفاظ بالحالة لمنطق النشاط التجاري. |
| استخدام ميزة جمع حالات واجهة المستخدم المتوافقة مع مراحل النشاط
يُنصح بشدة |
اجمع حالة واجهة المستخدم من واجهة المستخدم باستخدام أداة إنشاء الكوروتينات المناسبة المدرِكة لدورة الحياة، collectAsStateWithLifecycle.
مزيد من المعلومات حول |
| لا ترسِل الأحداث من ViewModel إلى واجهة المستخدم.
يُنصح بشدة |
معالجة الحدث على الفور في ViewModel وتعديل الحالة باستخدام نتيجة معالجة الحدث لمزيد من المعلومات عن أحداث واجهة المستخدم، اطّلِع على مقالة التعامل مع أحداث ViewModel. |
| استخدِم تطبيقًا بنشاط واحد.
يُنصح بشدة |
استخدِم التنقّل 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 مع طبقات البيانات أو النطاقات باستخدام ما يلي:
|
| استخدِم ViewModels على مستوى الشاشة.
يُنصح بشدة |
لا تستخدِم ViewModels في أجزاء واجهة المستخدم القابلة لإعادة الاستخدام. يجب استخدام ViewModels في ما يلي:
بالنسبة إلى العناصر القابلة للإنشاء الأكثر تعقيدًا أو تلك التي تتضمّن سلوكًا ديناميكيًا استنادًا إلى الحالة، استخدِم |
| استخدِم عناصر الاحتفاظ بالحالة العادية في مكوّنات واجهة المستخدم القابلة لإعادة الاستخدام.
يُنصح بشدة |
استخدِم عناصر الاحتفاظ بالحالة العادية للتعامل مع التعقيد في مكوّنات واجهة المستخدم القابلة لإعادة الاستخدام. عند إجراء ذلك، يمكن نقل الحالة والتحكّم فيها خارجيًا. |
لا تستخدِم AndroidViewModel.
يُنصح به |
استخدِم فئة ViewModel، وليس AndroidViewModel. لا تستخدِم الفئة Application في ViewModel. بدلاً من ذلك، يمكنك نقل العنصر التابع إلى واجهة المستخدِم أو طبقة البيانات. |
| عرض حالة واجهة المستخدم
يُنصح به |
اجعل ViewModels تعرض البيانات لواجهة المستخدم من خلال سمة واحدة تُسمى 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.
يُنصح بشدة |
لا تستبدِل طرق دورة حياة
|
يوضّح المقتطف التالي كيفية تنفيذ العمليات في حالة 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 إذا كان مشروعك معقّدًا بما يكفي، مثلاً إذا كان يتضمّن أيًا مما يلي:
|
الاختبار
في ما يلي بعض أفضل الممارسات المتعلّقة بالاختبار:
| الاقتراح | الوصف |
|---|---|
| تحديد ما يجب اختباره:
يُنصح بشدة |
ما لم يكن المشروع بسيطًا مثل تطبيق "hello world"، اختبِره. يجب تضمين ما يلي على الأقل:
|
| يجب تفضيل عمليات المحاكاة على عمليات التزوير.
يُنصح بشدة |
لمزيد من المعلومات حول استخدام العناصر الوهمية، يُرجى الاطّلاع على استخدام العناصر الوهمية في Android. |
| اختبار StateFlows
يُنصح بشدة |
عند اختبار StateFlow، اتّبِع الخطوات التالية:
|
لمزيد من المعلومات، يُرجى الاطّلاع على ما يجب اختباره في Android واختبار تصميم Compose.
الأداء
في ما يلي بعض أفضل الممارسات لتحسين الأداء:
| الاقتراح | الوصف |
|---|---|
| استخدِم R8 لتقليل حجم تطبيقك وتحسينه.
يُنصح بشدة |
فعِّل R8 في الوضع الكامل في إعدادات التصميم لإزالة الرموز البرمجية غير المستخدَمة وتشويش الفئات وإعادة كتابة الرموز البرمجية لتحسين أداء وقت التشغيل. |
| استخدام "ملفات تعريف بدء التشغيل" و"ملفات المرجع"
يُنصح بشدة |
إنشاء ملفات شخصية للمرجع وملفات تعريف بدء التشغيل لتجميع مسارات الرموز البرمجية المهمة مسبقًا يؤدي ذلك إلى تقليل وقت استجابة بدء التشغيل (TTID وTTFD) بشكل كبير والحدّ من أخطاء ANR والتشوّش. |
| اختبار الأداء باستخدام Macrobenchmark
يُنصح بشدة |
استخدِم مكتبة Macrobenchmark لاختبار أداء تطبيقك ومنع حدوث أخطاء. |
النماذج
اتّبِع أفضل الممارسات التالية عند تطوير النماذج في تطبيقاتك:
| الاقتراح | الوصف |
|---|---|
| إنشاء نموذج لكل طبقة في التطبيقات المعقّدة
يُنصح به |
في التطبيقات المعقّدة، أنشِئ نماذج جديدة في طبقات أو مكوّنات مختلفة عندما يكون ذلك منطقيًا. في ما يلي أمثلة:
|
اصطلاحات التسمية
عند تسمية قاعدة الرموز البرمجية، يجب مراعاة أفضل الممارسات التالية:
| الاقتراح | الوصف |
|---|---|
| طُرق التسمية
اختيارية |
استخدِم عبارات فعلية لتسمية الطرق، مثل makePayment(). |
| تسمية السمات
اختيارية |
استخدِم عبارات اسمية لتسمية السمات، مثل inProgressTopicSelection. |
| تسمية مصادر البيانات
اختيارية |
عندما تعرض فئة مصدر بيانات Flow أو أي مصدر بيانات آخر، يكون اصطلاح التسمية هو get{model}Stream، على سبيل المثال، getAuthorStream(): Flow<Author>.
إذا كانت الدالة تعرض قائمة بالنماذج، استخدِم اسم النموذج بصيغة الجمع: getAuthorsStream(): Flow<List<Author>>. |
| تنفيذ الواجهات التي تحمل أسماء
اختيارية |
استخدِم أسماء ذات دلالة لعمليات تنفيذ الواجهات. استخدِم Default كبادئة إذا لم تتمكّن من العثور على اسم أفضل. على سبيل المثال، بالنسبة إلى واجهة NewsRepository، قد يكون لديك OfflineFirstNewsRepository أو InMemoryNewsRepository. إذا لم تتمكّن من العثور على اسم مناسب، استخدِم DefaultNewsRepository.
ابدأ عمليات التنفيذ الوهمية بالبادئة Fake، كما في FakeAuthorsRepository. |
مراجع إضافية
لمزيد من المعلومات حول بنية Android، يُرجى الاطّلاع على المراجع الإضافية التالية: