Creare ViewModel con dipendenze (Views)   Parte di Android Jetpack.

Concetti e implementazione di Jetpack Compose

Seguendo le best practice dell'iniezione delle dipendenze, i ViewModel possono accettare dipendenze come parametri nel loro costruttore. Si tratta per lo più di tipi dei livelli didominio o dati. Poiché il framework fornisce i ViewModel, è necessario un meccanismo speciale per crearne le istanze. Questo meccanismo è l'interfaccia ViewModelProvider.Factory. Solo le implementazioni di questa interfaccia possono creare istanze di ViewModel nell'ambito corretto.

ViewModel con CreationExtras

Se una classe ViewModel riceve dipendenze nel suo costruttore, fornisci una factory che implementa l'interfaccia ViewModelProvider.Factory. Esegui l'override della create(Class<T>, CreationExtras) funzione per fornire una nuova istanza del ViewModel.

CreationExtras con APPLICATION_KEY

Di seguito è riportato un esempio di come fornire un'istanza di un ViewModel che accetta un repository con ambito alla classe Application e SavedStateHandle come dipendenze:

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

Dopodiché, puoi utilizzare questa factory quando recuperi un'istanza del 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
}

Trasmettere parametri personalizzati come CreationExtras

Puoi trasmettere le dipendenze al tuo ViewModel tramite CreationExtras creando una chiave personalizzata. Questa operazione può essere utile se il tuo ViewModel dipende da oggetti a cui non è possibile accedere tramite la classe Application e APPLICATION_KEY. Un esempio è quando il ViewModel viene creato all'interno di un modulo Kotlin Multiplatform e pertanto non ha accesso alle dipendenze di Android.

In questo esempio, il ViewModel definisce una chiave personalizzata e la utilizza in the 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,
                )
            }
        }
    }
}

Puoi creare un'istanza di un ViewModel con un CreationExtras.Key da un ViewModelStoreOwner come ComponentActivity, Fragment, o 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]

Factory per la versione di ViewModel precedente alla 2.5.0

Se utilizzi una versione di ViewModel precedente alla 2.5.0, devi fornire le factory da un sottoinsieme di classi che estendono ViewModelProvider.Factory e implementano la funzione create(Class<T>). A seconda delle dipendenze richieste da ViewModel, è necessario estendere una classe diversa:

Se Application o SavedStateHandle non sono necessari, estendi semplicemente da ViewModelProvider.Factory.

L'esempio seguente utilizza un AbstractSavedStateViewModelFactory per un ViewModel che accetta un repository e un tipo SavedStateHandle come dipendenza:

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

Dopodiché, puoi utilizzare la factory per recuperare il 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
}