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 una fábrica que implemente la interfaz ViewModelProvider.Factory
. Anula la función create(Class<T>, CreationExtras)
para proporcionar una instancia nueva 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 función CreationExtras.createSavedStateHandle()
.createSavedStateHandle()) y pásala al ViewModel.
A continuación, se muestra un ejemplo de cómo proporcionar una instancia de un ViewModel que toma un repositorio definido 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
)
}
}
}
}
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 extiendan 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