סקירה כללית של ViewModel‏ (Views)

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

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

היתרונות של ViewModel

החלופה ל-ViewModel היא מחלקה רגילה שמכילה את הנתונים שמוצגים בממשק המשתמש. הבעיה הזו יכולה לקרות כשעוברים בין פעילויות או בין יעדי ניווט. אם לא מאחסנים את הנתונים באמצעות מנגנון שמירת מצב המופע, הם נמחקים. ‫ViewModel מספק API נוח להתמדת נתונים, שפותר את הבעיה הזו.

יש שני יתרונות עיקריים לשימוש במחלקה ViewModel:

  • היא מאפשרת לכם לשמור את מצב ממשק המשתמש.
  • הוא מספק גישה ללוגיקה עסקית.

היקף

כשיוצרים מופע של ViewModel, מעבירים לו אובייקט שמטמיע את הממשק ViewModelStoreOwner. יכול להיות שזה יעד ניווט, גרף ניווט, פעילות, קטע או כל סוג אחר שמטמיע את הממשק. לאחר מכן, ה-ViewModel מוגדר בהיקף של Lifecycle של ViewModelStoreOwner. הוא נשאר בזיכרון עד שViewModelStoreOwner נעלם באופן סופי.

טווח של מחלקות הן מחלקות משנה ישירות או עקיפות של הממשק ViewModelStoreOwner. מחלקות המשנה הישירות הן ComponentActivity,‏ Fragment ו-NavBackStackEntry. רשימה מלאה של מחלקות משנה עקיפות זמינה בחומר העזר בנושא ViewModelStoreOwner.

הטמעה של ViewModel

הדוגמה הבאה היא הטמעה של ViewModel למסך שמאפשר למשתמש לגלגל קוביות.

Kotlin

data class DiceUiState(
    val firstDieValue: Int? = null,
    val secondDieValue: Int? = null,
    val numberOfRolls: Int = 0,
)

class DiceRollViewModel : ViewModel() {

    // Expose screen UI state
    private val _uiState = MutableStateFlow(DiceUiState())
    val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()

    // Handle business logic
    fun rollDice() {
        _uiState.update { currentState ->
            currentState.copy(
                firstDieValue = Random.nextInt(from = 1, until = 7),
                secondDieValue = Random.nextInt(from = 1, until = 7),
                numberOfRolls = currentState.numberOfRolls + 1,
            )
        }
    }
}

Java

public class DiceUiState {
    private final Integer firstDieValue;
    private final Integer secondDieValue;
    private final int numberOfRolls;

    // ...
}

public class DiceRollViewModel extends ViewModel {

    private final MutableLiveData<DiceUiState> uiState =
        new MutableLiveData(new DiceUiState(null, null, 0));
    public LiveData<DiceUiState> getUiState() {
        return uiState;
    }

    public void rollDice() {
        Random random = new Random();
        uiState.setValue(
            new DiceUiState(
                random.nextInt(7) + 1,
                random.nextInt(7) + 1,
                uiState.getValue().getNumberOfRolls() + 1
            )
        );
    }
}

אפשר לגשת ל-ViewModel מפעילות באופן הבא:

Kotlin

import androidx.activity.viewModels

class DiceRollActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same DiceRollViewModel instance created by the first activity.

        // Use the 'by viewModels()' Kotlin property delegate
        // from the activity-ktx artifact
        val viewModel: DiceRollViewModel by viewModels()
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect {
                    // Update UI elements
                }
            }
        }
    }
}

Java

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.
        DiceRollViewModel model = new ViewModelProvider(this).get(DiceRollViewModel.class);
        model.getUiState().observe(this, uiState -> {
            // update UI
        });
    }
}