יצירת ViewModels עם יחסי תלות (Views)   בארגז הכלים Android Jetpack.

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

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

‫ViewModels עם CreationExtras

אם מחלקה ViewModel מקבלת תלויות ב-constructor שלה, צריך לספק פקטורי (factory) שמטמיע את הממשק ViewModelProvider.Factory. מבטלים את ההגדרה של הפונקציה create(Class<T>, CreationExtras) כדי לספק מופע חדש של ViewModel.

‫CreationExtras עם APPLICATION_KEY

בדוגמה הבאה אפשר לראות איך מספקים מופע של ViewModel שמקבל מאגר בהיקף של המחלקה Application ו-SavedStateHandle כתלות:

import static androidx.lifecycle.SavedStateHandleSupport.createSavedStateHandle;
import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY;

import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.viewmodel.ViewModelInitializer;

public class MyViewModel extends ViewModel {

    public MyViewModel(
        MyRepository myRepository,
        SavedStateHandle savedStateHandle
    ) { /* Init ViewModel here */ }

    static final ViewModelInitializer<MyViewModel> initializer = new ViewModelInitializer<>(
        MyViewModel.class,
        creationExtras -> {
            MyApplication app = (MyApplication) creationExtras.get(APPLICATION_KEY);
            assert app != null;
            SavedStateHandle savedStateHandle = createSavedStateHandle(creationExtras);

            return new MyViewModel(app.getMyRepository(), savedStateHandle);
        }
    );
}

אחר כך אפשר להשתמש בפקטורי (factory) הזה כשמאחזרים מופע של ViewModel:

Kotlin

import androidx.activity.viewModels

class MyActivity : AppCompatActivity() {

    private val viewModel: MyViewModel by viewModels { MyViewModel.Factory }

    // Rest of Activity code
}

Java

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;

public class MyActivity extends AppCompatActivity {

MyViewModel myViewModel = new ViewModelProvider(
    this,
    ViewModelProvider.Factory.from(MyViewModel.initializer)
).get(MyViewModel.class);

// Rest of Activity code
}

העברה של פרמטרים מותאמים אישית כ-CreationExtras

אפשר להעביר תלויות אל ViewModel דרך CreationExtras על ידי יצירת מפתח מותאם אישית. האפשרות הזו שימושית אם ViewModel תלוי באובייקטים שלא נגישים דרך המחלקה Application ו-APPLICATION_KEY. דוגמה למצב כזה היא כש-ViewModel נוצר בתוך מודול Kotlin Multiplatform ולכן אין לו גישה לתלות ב-Android.

בדוגמה הזו, ViewModel מגדיר מפתח מותאם אישית ומשתמש בו ב-ViewModelProvider.Factory.

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory

class MyViewModel(
    private val myRepository: MyRepository,
) : ViewModel() {
    // ViewModel logic

    // Define ViewModel factory in a companion object
    companion object {

        // Define a custom key for your dependency
        val MY_REPOSITORY_KEY = object : CreationExtras.Key<MyRepository> {}

        val Factory: ViewModelProvider.Factory = viewModelFactory {
            initializer {
                // Get the dependency in your factory
                val myRepository = this[MY_REPOSITORY_KEY] as MyRepository
                MyViewModel(
                    myRepository = myRepository,
                )
            }
        }
    }
}

אפשר ליצור מופע של ViewModel עם CreationExtras.Key מתוך ViewModelStoreOwner כמו ComponentActivity,‏ Fragment או NavBackStackEntry.

import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.MutableCreationExtras
// ...
    // Use from ComponentActivity, Fragment, NavBackStackEntry,
    // or another ViewModelStoreOwner.
    val viewModelStoreOwner: ViewModelStoreOwner = this
    val myViewModel: MyViewModel = ViewModelProvider.create(
        viewModelStoreOwner,
        factory = MyViewModel.Factory,
        extras = MutableCreationExtras().apply {
            set(MyViewModel.MY_REPOSITORY_KEY, myRepository)
        },
    )[MyViewModel::class]

‫Factories לגרסת ViewModel ישנה יותר מ-2.5.0

אם אתם משתמשים בגרסה של ViewModel שקודמת לגרסה 2.5.0, אתם צריכים לספק factories מקבוצת משנה של מחלקות שמרחיבות את ViewModelProvider.Factory ומיישמות את הפונקציה create(Class<T>). בהתאם לתלות של ViewModel, צריך להרחיב מחלקה אחרת מתוך:

אם לא צריך את Application או SavedStateHandle, פשוט מאריכים מ-ViewModelProvider.Factory.

בדוגמה הבאה נעשה שימוש ב-AbstractSavedStateViewModelFactory עבור ViewModel שמקבל מאגר וסוג SavedStateHandle כתלות:

Kotlin

class MyViewModel(
private val myRepository: MyRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {

// ViewModel logic ...

// Define ViewModel factory in a companion object
companion object {
    fun provideFactory(
        myRepository: MyRepository,
        owner: SavedStateRegistryOwner,
        defaultArgs: Bundle? = null,
    ): AbstractSavedStateViewModelFactory =
        object : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
            @Suppress("UNCHECKED_CAST")
            override fun <T : ViewModel> create(
                key: String,
                modelClass: Class<T>,
                handle: SavedStateHandle
            ): T {
                return MyViewModel(myRepository, handle) as T
            }
        }
    }
}

Java

import androidx.annotation.NonNull;
import androidx.lifecycle.AbstractSavedStateViewModelFactory;
import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModel;

public class MyViewModel extends ViewModel {
    public MyViewModel(
        MyRepository myRepository,
        SavedStateHandle savedStateHandle
    ) { /* Init ViewModel here */ }
}

public class MyViewModelFactory extends AbstractSavedStateViewModelFactory {

    private final MyRepository myRepository;

    public MyViewModelFactory(
        MyRepository myRepository
    ) {
        this.myRepository = myRepository;
    }

    @SuppressWarnings("unchecked")
    @NonNull
    @Override
    protected <T extends ViewModel> T create(
        @NonNull String key, @NonNull Class<T> modelClass, @NonNull SavedStateHandle handle
    ) {
        return (T) new MyViewModel(myRepository, handle);
    }
}

לאחר מכן, אפשר להשתמש ב-factory כדי לאחזר את ה-ViewModel:

Kotlin

import androidx.activity.viewModels

class MyActivity : AppCompatActivity() {

    private val viewModel: MyViewModel by viewModels {
        MyViewModel.provideFactory((application as MyApplication).myRepository, this)
    }

    // Rest of Activity code
}

Java

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;

public class MyActivity extends AppCompatActivity {

    MyViewModel myViewModel = new ViewModelProvider(
        this,
        ViewModelProvider.Factory.from(MyViewModel.initializer)
    ).get(MyViewModel.class);

    // Rest of Activity code
}