Cómo crear ViewModels con dependencias Parte de Android Jetpack.
Según las prácticas recomendadas para insertar dependencias, los ViewModels pueden tomar dependencias como parámetros en su constructor. En su mayoría, son de los tipos de capas de dominio o datos. Debido a que el framework proporciona los ViewModels, se requiere un mecanismo especial para crear instancias de ellos. Ese mecanismo es la interfaz ViewModelProvider.Factory
. Solo las implementaciones de esta interfaz pueden crear instancias de ViewModels en el alcance correcto.
ViewModels con CreationExtras
Si una clase ViewModel
recibe dependencias en su constructor, proporciona un
que implementa la interfaz ViewModelProvider.Factory
.
Anula la función create(Class<T>, CreationExtras)
para proporcionar un
nueva instancia del ViewModel.
CreationExtras
te permite acceder a información relevante que te ayuda a crear una instancia de ViewModel. Esta es una lista de claves a las que se puede acceder desde los extras:
Clave | Funcionalidad |
---|---|
ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY |
Proporciona acceso a la clave personalizada que pasaste a ViewModelProvider.get() . |
ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY |
Proporciona acceso a la instancia de la clase Application . |
SavedStateHandleSupport.DEFAULT_ARGS_KEY |
Proporciona acceso al paquete de argumentos que debes usar para construir un SavedStateHandle . |
SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY |
Proporciona acceso al SavedStateRegistryOwner que se usa para construir el ViewModel . |
SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY |
Proporciona acceso al ViewModelStoreOwner que se usa para construir el ViewModel . |
Para crear una instancia nueva de SavedStateHandle
, usa la
CreationExtras.createSavedStateHandle()
y pasarla al ViewModel.
CreationExtras con APPLICATION_KEY
El siguiente es un ejemplo de cómo proporcionar una instancia de un ViewModel
que
toma un repositorio con alcance para la clase Application
y
SavedStateHandle
como dependencias:
Kotlin
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.CreationExtras
class MyViewModel(
private val myRepository: MyRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
// ViewModel logic
// ...
// Define ViewModel factory in a companion object
companion object {
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
extras: CreationExtras
): T {
// Get the Application object from extras
val application = checkNotNull(extras[APPLICATION_KEY])
// Create a SavedStateHandle for this ViewModel from extras
val savedStateHandle = extras.createSavedStateHandle()
return MyViewModel(
(application as MyApplication).myRepository,
savedStateHandle
) as T
}
}
}
}
Java
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);
}
);
}
Luego, puedes usar esta fábrica cuando recuperas una instancia 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
}
Jetpack Compose
import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
modifier: Modifier = Modifier,
viewModel: MyViewModel = viewModel(factory = MyViewModel.Factory)
) {
// ...
}
Como alternativa, puedes usar el DSL de fábrica de ViewModel
para crear fábricas con una API de Kotlin más idiomática:
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
class MyViewModel(
private val myRepository: MyRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
// ViewModel logic
// Define ViewModel factory in a companion object
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val savedStateHandle = createSavedStateHandle()
val myRepository = (this[APPLICATION_KEY] as MyApplication).myRepository
MyViewModel(
myRepository = myRepository,
savedStateHandle = savedStateHandle
)
}
}
}
}
Pasa parámetros personalizados como CreationExtras
Puedes pasar dependencias a tu ViewModel
a través de CreationExtras
si
creas una clave personalizada.
Esto puede ser útil si tu ViewModel
depende de objetos que no están
y se puede acceder a través de la clase Application
y APPLICATION_KEY
. Un ejemplo de
Esto ocurre cuando tu ViewModel
se crea dentro de un Kotlin
módulo multiplataforma y, por lo tanto, no tiene acceso a las dependencias de Android.
En este ejemplo, ViewModel
define una clave personalizada y la usa en el
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,
)
}
}
}
}
Puedes crear una instancia de ViewModel
con CreationExtras.Key
de un
ViewModelStoreOwner
como
ComponentActivity
, Fragment
o NavBackStackEntry
, o con
Jetpack Compose
Kotlin
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]
Jetpack Compose
import androidx.lifecycle.viewmodel.MutableCreationExtras
import androidx.lifecycle.viewmodel.compose.viewModel
// ...
@Composable
fun MyApp(myRepository: MyRepository) {
val extras = MutableCreationExtras().apply {
set(MyViewModel.MY_REPOSITORY_KEY, myRepository)
}
val viewModel: MyViewModel = viewModel(
factory = MyViewModel.Factory,
extras = extras,
)
}
Fábricas para una versión de ViewModel anterior a la 2.5.0
Si usas una versión de ViewModel
anterior a la 2.5.0, debes proporcionar
fábricas de un subconjunto de clases que extienden ViewModelProvider.Factory
e implementar la función create(Class<T>)
. Según las dependencias que necesite ViewModel
, se debe extender una clase diferente:
- Desde
AndroidViewModelFactory
si se necesita la claseApplication
. - Desde
AbstractSavedStateViewModelFactory
si se debe pasarSavedStateHandle
como dependencia.
Si Application
o SavedStateHandle
no son necesarios, solo debes extenderlos desde ViewModelProvider.Factory
.
En el siguiente ejemplo, se usa un AbstractSavedStateViewModelFactory
para un ViewModel que toma un repositorio y un tipo SavedStateHandle
como dependencia:
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);
}
}
Luego, puedes usar la fábrica para recuperar tu 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
}
Jetpack Compose
import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
modifier: Modifier = Modifier,
viewModel: MyViewModel = viewModel(
factory = MyViewModel.provideFactory(
(LocalContext.current.applicationContext as MyApplication).myRepository,
owner = LocalSavedStateRegistryOwner.current
)
)
) {
// ...
}
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Módulo de estado guardado para ViewModel
- Cómo guardar estados de la IU
- Descripción general de LiveData