Tworzenie obiektów ViewModel z zależnościami (widoki), części Androida Jetpack.

Pojęcia i implementacja w Jetpack Compose

Zgodnie ze sprawdzonymi metodami wstrzykiwania zależności obiekty ViewModel mogą przyjmować zależności jako parametry w konstruktorze. Są to głównie typy z warstw domeny lub danych. Ponieważ framework udostępnia obiekty ViewModel, do tworzenia ich instancji jest potrzebny specjalny mechanizm. Tym mechanizmem jest ViewModelProvider.Factory interfejs. Tylko implementacje tego interfejsu mogą tworzyć instancje ViewModel w odpowiednim zakresie.

ViewModel z CreationExtras

Jeśli klasa ViewModel otrzymuje zależności w konstruktorze, podaj fabrykę, która implementuje interfejs ViewModelProvider.Factory. Zastąp funkcję create(Class<T>, CreationExtras), aby utworzyć nową instancję ViewModel.

CreationExtras z APPLICATION_KEY

Oto przykład sposobu udostępniania instancji ViewModel, która przyjmuje repozytorium w zakresie klasy ApplicationSavedStateHandle jako zależności:

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);
        }
    );
}

Następnie możesz użyć tej fabryki podczas pobierania instancji 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
}

Przekazywanie parametrów niestandardowych jako CreationExtras

Zależności możesz przekazywać do ViewModel za pomocą CreationExtras, tworząc klucz niestandardowy. Może to być przydatne, jeśli ViewModel zależy od obiektów, które nie są dostępne za pomocą klasy ApplicationAPPLICATION_KEY. Przykładem takiej sytuacji jest utworzenie ViewModel w module Kotlin Multiplatform, który nie ma dostępu do zależności Androida.

W tym przykładzie element ViewModel definiuje klucz niestandardowy i używa go w elemencie 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,
                )
            }
        }
    }
}

Możesz utworzyć instancję ViewModelCreationExtras.KeyViewModelStoreOwner, np. ComponentActivity, Fragment lub 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]

Fabryki dla ViewModel w wersji starszej niż 2.5.0

Jeśli używasz wersji ViewModel starszej niż 2.5.0, musisz podać fabryki z podzbioru klas, które rozszerzają ViewModelProvider.Factory, i zastosować funkcję create(Class<T>). W zależności od tego, jakich zależności potrzebuje ViewModel, należy rozszerzyć inną klasę:

Jeśli nie potrzebujesz Application ani SavedStateHandle, po prostu przedłuż od ViewModelProvider.Factory.

W przykładzie poniżej użyto AbstractSavedStateViewModelFactory w przypadku elementu ViewModel, który przyjmuje repozytorium i typ SavedStateHandle jako zależność:

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);
    }
}

Następnie możesz użyć fabryki, aby pobrać 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
}