המלצות לארכיטקטורה של Android (תצוגות)

מושגים ויישום ב-Jetpack פיתוח נייטיב

בדף הזה מפורטות כמה שיטות מומלצות והמלצות לארכיטקטורה. כדאי להשתמש בהם כדי לשפר את האיכות, היציבות והגמישות של האפליקציה. הם גם מקלים על התחזוקה והבדיקה של האפליקציה.

שכבת ממשק המשתמש

התפקיד של שכבת ממשק המשתמש הוא להציג את נתוני האפליקציה במסך ולשמש כנקודת האינטראקציה העיקרית של המשתמש. ריכזנו כאן כמה שיטות מומלצות לשכבת ממשק המשתמש:

  • מומלץ ליצור מאגרי מידע גם אם הם מכילים רק מקור נתונים אחד.
  • באפליקציות קטנות, אפשר לבחור למקם סוגים של שכבת נתונים ב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.

מומלץ מאוד

מודלים של תצוגה לא צריכים להכיל הפניה לסוג שקשור למחזור החיים. אל תעבירו את Activity, Fragment,‏ Context או Resources כתלות. אם משהו דורש Context ב-ViewModel, כדאי לבדוק היטב אם הוא נמצא בשכבה הנכונה.

משתמשים בקורוטינות וב-Flows.

מומלץ מאוד

ה-ViewModel מקיים אינטראקציה עם שכבות הנתונים או הדומיין באמצעות:

  • ‫Kotlin flows לקבלת נתוני אפליקציה,
  • פונקציות של suspend כדי לבצע פעולות באמצעות viewModelScope.

משתמשים ב-ViewModels ברמת המסך.

מומלץ מאוד

אל תשתמשו ב-ViewModels בחלקים של ממשק משתמש שאפשר לעשות בהם שימוש חוזר. כדאי להשתמש ב-ViewModels במקרים הבאים:

  • פעילויות/קטעים בתצוגות מפורטות
  • יעדים או תרשימים כשמשתמשים ב-Jetpack Navigation.

אל תשתמשו ב-AndroidViewModel.

מומלץ מאוד

משתמשים במחלקת ViewModel ולא במחלקת AndroidViewModel. אסור להשתמש במחלקה Application ב-ViewModel. במקום זאת, מעבירים את התלות לממשק המשתמש או לשכבת הנתונים.

חשיפת מצב של ממשק משתמש.

מומלץ

מודלים של תצוגה צריכים לחשוף נתונים לממשק המשתמש באמצעות מאפיין יחיד שנקרא 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 ב-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) {
                // ...
            }
        }
    }
}