בדף הזה מוצגות כמה שיטות מומלצות והמלצות בנושא ארכיטקטורה. כדאי להשתמש בהם כדי לשפר את האיכות, היציבות והגמישות של האפליקציה. הם גם מקלים על התחזוקה והבדיקה של האפליקציה.
השיטות המומלצות שבהמשך מחולקות לפי נושאים. לכל המלצה יש עדיפות שמשקפת את מידת החשיבות שלה. רשימת העדיפויות היא כדלקמן:
- מומלץ מאוד: כדאי ליישם את השיטה הזו, אלא אם היא סותרת באופן מהותי את הגישה שלכם.
- מומלץ: סביר להניח שהשיטה הזו תשפר את האפליקציה.
- אופציונלי: במקרים מסוימים, שימוש בשיטה הזו יכול לשפר את האפליקציה.
ארכיטקטורה שכבתית
הארכיטקטורה השכבתית המומלצת שלנו תומכת בהפרדה בין תחומים. הוא מניע את ממשק המשתמש ממודלים של נתונים, עומד בדרישות של עקרון המקור המרוכז, ופועל לפי עקרונות של זרימת נתונים חד-כיוונית. ריכזנו כאן כמה שיטות מומלצות לארכיטקטורה שכבתית:
| המלצה | תיאור |
|---|---|
| משתמשים בשכבת נתונים מוגדרת בבירור.
מומלץ מאוד |
שכבת הנתונים חושפת את נתוני האפליקציה לשאר האפליקציה, והיא מכילה את רוב הלוגיקה העסקית של האפליקציה.
|
| משתמשים בשכבת ממשק משתמש מוגדרת בבירור.
מומלץ מאוד |
שכבת ממשק המשתמש מציגה את נתוני האפליקציה במסך ומשמשת כנקודת האינטראקציה העיקרית של המשתמש. Jetpack Compose היא ערכת הכלים המודרנית המומלצת לבניית ממשק המשתמש של האפליקציה.
|
| חשיפת נתוני אפליקציה משכבת הנתונים באמצעות מאגר.
מומלץ מאוד |
מוודאים שרכיבים בשכבת ממשק המשתמש, כמו פונקציות Composable או ViewModels, לא מקיימים אינטראקציה ישירה עם מקור נתונים. דוגמאות למקורות נתונים:
|
| שימוש בקורוטינות וברצפי פעולות.
מומלץ מאוד |
משתמשים בקורוטינות וב-Flows כדי לתקשר בין השכבות.
מידע נוסף על שיטות מומלצות לשימוש בקורוטינות זמין במאמר בנושא שיטות מומלצות לשימוש בקורוטינות ב-Android. |
| משתמשים בשכבת דומיין.
מומלץ באפליקציות גדולות |
משתמשים בשכבת דומיין עם תרחישי שימוש אם רוצים לעשות שימוש חוזר בלוגיקה עסקית שמתקשרת עם שכבת הנתונים בכמה ViewModels, או אם רוצים לפשט את המורכבות של הלוגיקה העסקית של ViewModel מסוים |
שכבת ממשק המשתמש
התפקיד של שכבת ממשק המשתמש הוא להציג את נתוני האפליקציה במסך ולשמש כנקודת האינטראקציה העיקרית של המשתמש. ריכזנו כאן כמה שיטות מומלצות לשכבת ממשק המשתמש:
| המלצה | תיאור |
|---|---|
| פועלים לפי זרימת נתונים חד-כיוונית (UDF).
מומלץ מאוד |
פועלים לפי העקרונות של זרימת נתונים חד-כיוונית (UDF), שבהם ViewModels חושפים את מצב ממשק המשתמש באמצעות תבנית הצופה ומקבלים פעולות מממשק המשתמש באמצעות קריאות לשיטות. |
| כדאי להשתמש ב-AAC ViewModels אם היתרונות שלהם רלוונטיים לאפליקציה שלכם.
מומלץ מאוד |
משתמשים ב-AAC ViewModels כדי לטפל בלוגיקה העסקית, ומאחזרים נתונים של האפליקציה כדי לחשוף את מצב ממשק המשתמש לממשק המשתמש.
מידע נוסף על שיטות מומלצות לשימוש ב-ViewModel זמין במאמר המלצות לארכיטקטורה. מידע נוסף על היתרונות של ViewModels זמין במאמר The ViewModel as a business logic state holder (ה-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 במקרים הבאים:
כדי להגדיר ViewModel עבור קומפוזיציות מורכבות יותר, או כאלה עם התנהגות דינמית שמבוססת על מצב, משתמשים ב- |
| משתמשים במחזיקי מצב פשוטים ברכיבי ממשק משתמש שאפשר לעשות בהם שימוש חוזר.
מומלץ מאוד |
כדי לטפל במורכבות ברכיבי ממשק משתמש שאפשר להשתמש בהם שוב, כדאי להשתמש במחלקות פשוטות של מחזיקי מצב. כשעושים את זה, אפשר להעלות את המצב ולשלוט בו מבחוץ. |
אל תשתמשו ב-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
)
// ...
}
מחזור חיים
כדאי לפעול לפי השיטות המומלצות לעבודה עם מחזור החיים של פעילות:
| המלצה | תיאור |
|---|---|
מומלץ להשתמש באפקטים שמודעים למחזור החיים ברכיבים קומפוזביליים במקום להחליף את הקריאות החוזרות (callback) של מחזור החיים של Activity.
מומלץ מאוד |
אל תבטלו את השיטות של מחזור החיים
|
בקטע הקוד הבא אפשר לראות איך לבצע פעולות בהינתן מצב מחזור חיים מסוים:
@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 אם הפרויקט מורכב מספיק – לדוגמה, אם הוא כולל אחד מהפריטים הבאים:
|
בדיקה
ריכזנו כאן כמה שיטות מומלצות לבדיקה:
| המלצה | תיאור |
|---|---|
| מה כדאי לבדוק
מומלץ מאוד |
אלא אם הפרויקט פשוט כמו אפליקציית 'שלום עולם', כדאי לבדוק אותו. לכל הפחות, צריך לכלול את הפרטים הבאים:
|
| עדיף להשתמש ב-fakes במקום ב-mocks.
מומלץ מאוד |
מידע נוסף על השימוש ב-fakes זמין במאמר שימוש ב-test doubles ב-Android. |
| בדיקת StateFlows.
מומלץ מאוד |
כשבודקים את StateFlow, מבצעים את הפעולות הבאות:
|
מידע נוסף זמין במאמרים מה כדאי לבדוק ב-Android ובדיקת פריסת פיתוח נייטיב.
ביצועים
ריכזנו כאן כמה שיטות מומלצות לשיפור הביצועים:
| המלצה | תיאור |
|---|---|
| כדאי להשתמש ב-R8 כדי לצמצם את גודל האפליקציה ולבצע אופטימיזציה.
מומלץ מאוד |
כדי להסיר קוד שלא נמצא בשימוש, לעשות ערפול קוד למחלקות ולשכתב קוד כדי לשפר את הביצועים בזמן הריצה, צריך להפעיל את R8 במצב מלא ב<b>תצורת build</b> שלכם. |
| הטמעה של פרופילים של Baseline ופרופילים להפעלה.
מומלץ מאוד |
יוצרים פרופילי Baseline ופרופילים של הפעלה כדי לבצע קומפילציה מראש של נתיבי קוד קריטיים. הפעולה הזו מפחיתה באופן משמעותי את זמן האחזור של תהליך ההפעלה (TTID ו-TTFD) ומצמצמת את הבעיות בממשק (jank) ואת שגיאות ה-ANR. |
| בודקים את הביצועים באמצעות Macrobenchmark.
מומלץ מאוד |
כדי לבדוק את הביצועים של האפליקציה ולמנוע רגרסיות, אפשר להשתמש בספריית Macrobenchmark. |
מודלים
כדאי לפעול לפי השיטות המומלצות הבאות כשמפתחים מודלים באפליקציות:
| המלצה | תיאור |
|---|---|
| באפליקציות מורכבות, כדאי ליצור מודל לכל שכבה.
מומלץ |
באפליקציות מורכבות, כדאי ליצור מודלים חדשים בשכבות או ברכיבים שונים כשזה הגיוני. מומלץ להביא בחשבון את הדוגמאות הבאות:
|
מוסכמות למתן שמות
כשנותנים שם לבסיס הקוד, כדאי לפעול לפי השיטות המומלצות הבאות:
| המלצה | תיאור |
|---|---|
| שיטות למתן שמות.
אופציונלי |
משתמשים בצירופי פועל כדי לתת שמות לשיטות – לדוגמה, makePayment(). |
| מתן שמות למאפיינים.
אופציונלי |
משתמשים בצירופי שם עצם כדי לתת שמות למאפיינים – לדוגמה, inProgressTopicSelection. |
| מתן שמות למקורות נתונים.
אופציונלי |
כשמחלקה חושפת זרם Flow או כל זרם אחר, מוסכמת השמות היא get{model}Stream – לדוגמה, getAuthorStream(): Flow<Author>.
אם הפונקציה מחזירה רשימה של מודלים, צריך להשתמש בשם המודל ברבים: getAuthorsStream(): Flow<List<Author>>. |
| מתן שמות להטמעות של ממשקים.
אופציונלי |
כדאי להשתמש בשמות בעלי משמעות להטמעות של ממשקים. אם לא מוצאים שם טוב יותר, משתמשים ב-Default כקידומת. לדוגמה, בממשק NewsRepository יכול להיות OfflineFirstNewsRepository או InMemoryNewsRepository. אם לא מוצאים שם טוב, אפשר להשתמש ב-DefaultNewsRepository.
צריך להוסיף את הקידומת Fake להטמעות מזויפות, כמו בדוגמה FakeAuthorsRepository. |
מקורות מידע נוספים
למידע נוסף על ארכיטקטורת Android, אפשר להיעזר במקורות המידע הנוספים הבאים: