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