בדף הזה מוצגות כמה שיטות מומלצות והמלצות בנושא ארכיטקטורה. כדאי להשתמש בהם כדי לשפר את האיכות, העמידות והיכולת להתאמה של האפליקציה. הם גם מאפשרים לתחזק ולבדוק את האפליקציה בקלות רבה יותר.
השיטות המומלצות שמפורטות בהמשך מקובצות לפי נושאים. לכל אחת מהן יש עדיפות שמשקפת את מידת ההמלצה של הצוות. רשימת העדיפויות היא:
- מומלץ מאוד: כדאי להטמיע את השיטה הזו, אלא אם היא מנוגדת באופן מהותי לגישה שלכם.
- מומלץ: השיטה הזו תשפר את האפליקציה.
- אופציונלי: השיטה הזו יכולה לשפר את האפליקציה בנסיבות מסוימות.
ארכיטקטורה שכבתית
הארכיטקטורה המומלצת בשכבות שלנו מעודדת הפרדה בין תחומי העניין. הוא מבוסס על מודלים של נתונים, עומד בעיקרון של מקור מרוכז אחד ופועל לפי העקרונות של זרימת נתונים חד-כיוונית. ריכזנו כאן כמה שיטות מומלצות לארכיטקטורה שכבתית:
המלצה | תיאור |
---|---|
להשתמש בשכבת נתונים מוגדרת בבירור.
מומלץ מאוד |
שכבת הנתונים חושפת את נתוני האפליקציה לשאר האפליקציה, ומכילה את רוב הלוגיקה העסקית של האפליקציה.
|
להשתמש בשכבת ממשק משתמש מוגדרת בבירור.
מומלץ מאוד |
בשכבת ממשק המשתמש מוצגים נתוני האפליקציה במסך, והיא משמשת כנקודת האינטראקציה העיקרית של המשתמש.
|
שכבת הנתונים צריכה לחשוף את נתוני האפליקציה באמצעות מאגר.
מומלץ מאוד |
רכיבים בשכבת ממשק המשתמש, כמו רכיבים מורכבים, פעילויות או ViewModels, לא אמורים לקיים אינטראקציה ישירה עם מקור נתונים. דוגמאות למקורות נתונים:
|
שימוש בפונקציות רפיטיביות (coroutines) ובתהליכים (flows).
מומלץ מאוד |
משתמשים בפונקציות רפליקות (coroutines) ובזרמים (flows) כדי לתקשר בין השכבות. |
להשתמש בשכבת דומיין.
מומלץ באפליקציות גדולות |
שימוש בשכבת דומיין, תרחישים לדוגמה, אם אתם צריכים לעשות שימוש חוזר בלוגיקה עסקית שמקיימת אינטראקציה עם שכבת הנתונים במספר מודלים של ViewModel, או אם אתם רוצים לפשט את המורכבות של הלוגיקה העסקית ב-ViewModel מסוים |
שכבה בממשק המשתמש
התפקיד של שכבת ממשק המשתמש הוא להציג את נתוני האפליקציה במסך ולשמש כנקודת האינטראקציה הראשית של המשתמש. ריכזנו כאן כמה שיטות מומלצות לשכבת ממשק המשתמש:
המלצה | תיאור |
---|---|
פועלים לפי תנועת נתונים חד-כיוונית (UDF).
מומלץ מאוד |
פועלים לפי העקרונות של זרימת נתונים חד-כיוונית (UDF), שבהם מודלים של תצוגת מידע חושפים את מצב ממשק המשתמש באמצעות תבנית הצופה ומקבלים פעולות מממשק המשתמש באמצעות קריאות ל-method. |
כדאי להשתמש ב-ViewModels של AAC אם היתרונות שלהם רלוונטיים לאפליקציה שלכם.
מומלץ מאוד |
משתמשים ב-ViewModels של AAC כדי לטפל בלוגיקה העסקית, ומאחזרים את נתוני האפליקציה כדי לחשוף את מצב ממשק המשתמש לממשק המשתמש (Compose או תצוגות Android). |
שימוש באוסף המצבים של ממשק המשתמש תוך התייחסות למחזור החיים.
מומלץ מאוד |
איסוף המצב של ממשק המשתמש ממשק המשתמש באמצעות ה-builder המתאים של פונקציית הריצה המשותפת (coroutine) עם תמיכה במחזור חיים: repeatOnLifecycle במערכת View ו-collectAsStateWithLifecycle ב-Jetpack Compose.
מידע נוסף על מידע נוסף על |
אין לשלוח אירועים מ-ViewModel לממשק המשתמש.
מומלץ מאוד |
עיבוד האירוע באופן מיידי ב-ViewModel וגרימה לעדכון המצב עם תוצאת הטיפול באירוע. מידע נוסף על אירועים בממשק המשתמש |
להשתמש באפליקציה של פעילות יחידה.
מומלץ |
אם לאפליקציה יש יותר ממסך אחד, אפשר להשתמש בקטעי ניווט או בכתיבת ניווט כדי לנווט בין מסכים וקישור עומק לאפליקציה. |
להשתמש ב-Jetpack פיתוח נייטיב.
המלצות |
אתם יכולים להשתמש ב-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()
}
ViewModel
ViewModels אחראים על מתן מצב ממשק המשתמש וגישה לשכבת הנתונים. ריכזנו כאן כמה שיטות מומלצות ל-ViewModels:
המלצה | תיאור |
---|---|
מודל ה-ViewModel לא צריך להיות תלוי במחזור החיים של Android.
מומלץ מאוד |
מודל תצוגה לא צריך להכיל הפניה לסוג כלשהו שקשור למחזור חיים. אין להעביר את Activity, Fragment, Context או Resources כיחסי תלות.
אם משהו צריך Context ב-ViewModel, כדאי לבדוק היטב אם הוא נמצא בשכבה הנכונה. |
שימוש בפונקציות רפיטיביות (coroutines) ובתהליכים (flows).
מומלץ מאוד |
ה-ViewModel מקיים אינטראקציה עם הנתונים או עם שכבות הדומיין באמצעות:
|
שימוש ב-ViewModels ברמת המסך.
מומלץ מאוד |
אל תשתמשו ב-ViewModels בחלקים של ממשק המשתמש שניתנים לשימוש חוזר. מומלץ להשתמש ב-ViewModels במקרים הבאים:
|
משתמשים בכיתות פשוטות של מאגרי מצב ברכיבי ממשק משתמש לשימוש חוזר.
מומלץ מאוד |
משתמשים בכיתות פשוטות של מאגרי מצב כדי לטפל במורכבות ברכיבי ממשק משתמש לשימוש חוזר. כך אפשר להעלות את המצב ולשלוט בו באופן חיצוני. |
אין להשתמש ב-AndroidViewModel .
מומלץ |
משתמשים בכיתה ViewModel ולא בכיתה AndroidViewModel . אסור להשתמש בכיתה Application ב-ViewModel. במקום זאת, צריך להעביר את התלות לממשק המשתמש או לשכבת הנתונים. |
חשיפת מצב של ממשק משתמש.
המלצות |
מודלים של תצוגה צריכים לחשוף נתונים לממשק המשתמש דרך נכס יחיד שנקרא 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:
המלצה | תיאור |
---|---|
אל תשנו את שיטות מחזור החיים ב-Activities או ב-Fragments.
מומלץ מאוד |
אין לשנות את שיטות מחזור החיים, כמו onResume , ב-Activities או ב-Fragments. במקום זאת, צריך להשתמש ב-LifecycleObserver . אם האפליקציה צריכה לבצע פעולה כשמחזור החיים מגיע ל-Lifecycle.State מסוים, צריך להשתמש ב-API של 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. לדוגמה, אם יש לכם:
|
בדיקה
ריכזנו כאן כמה שיטות מומלצות לבדיקה:
המלצה | תיאור |
---|---|
מה כדאי לבדוק
מומלץ מאוד |
אלא אם הפרויקט פשוט כמו אפליקציית hello world, כדאי לבדוק אותו, לפחות באמצעות:
|
עדיף להשתמש בזיופים על פני חיקויים.
מומלץ מאוד |
מידע נוסף זמין במאמר שימוש במכפילים של בדיקה במסמכי התיעוד של Android. |
בדיקת StateFlows.
מומלץ מאוד |
כשבודקים את StateFlow :
|
מידע נוסף זמין במדריך מה צריך לבדוק ב-Android DAC.
דגמים
כשמפתחים מודלים באפליקציות, כדאי לפעול לפי השיטות המומלצות הבאות:
המלצה | תיאור |
---|---|
ליצור מודל לכל שכבה באפליקציות מורכבות.
מומלץ |
באפליקציות מורכבות, כדאי ליצור מודלים חדשים בשכבות או ברכיבים שונים כשזה הגיוני. ריכזנו כאן כמה דוגמאות:
|
מוסכמות למתן שמות
כשנותנים שם למאגר הקוד, כדאי לפעול לפי השיטות המומלצות הבאות:
המלצה | תיאור |
---|---|
שיטות למתן שמות.
אופציונלי |
שיטות צריכות להיות ביטוי פועל. לדוגמה, makePayment() . |
מתן שמות לנכסים.
אופציונלי |
המאפיינים צריכים להיות ביטוי של שם עצם. לדוגמה: inProgressTopicSelection . |
מתן שמות למקורות נתונים.
אופציונלי |
כשכיתה חושפת מקור נתונים מסוג Flow, LiveData או מקור נתונים אחר, שם המקור מתחיל ב-get{model}Stream() . לדוגמה, getAuthorStream(): Flow<Author>
אם הפונקציה מחזירה רשימה של מודלים, שם המודל צריך להיות בלשון רבים: getAuthorsStream(): Flow<List<Author>> |
שמות של ממשקי הטמעה.
אופציונלי |
השמות של הטמעות הממשקים צריכים להיות משמעותיים. אם לא ניתן למצוא שם טוב יותר, צריך להשתמש ב-Default כתחילית. לדוגמה, עבור ממשק NewsRepository , יכול להיות OfflineFirstNewsRepository או InMemoryNewsRepository . אם לא מצאת שם טוב, אפשר להשתמש ב-DefaultNewsRepository .
להטמעות מזויפות צריכה להיות קידומת Fake , כמו FakeAuthorsRepository . |