Je suis la documentation this pour en savoir plus sur LiveData et ViewModel .
public class UserModel extends ViewModel {
private MutableLiveData<User> user;
@Inject UserModel(MutableLiveData<User> user) {
this.user = user;
}
public void init() {
if (this.user != null) {
return;
}
this.user = new MutableLiveData<>();
}
public MutableLiveData<User> getUser() {
return user;
}
}
Cependant, lorsque je lance le code, j'obtiens une exception:
final UserViewModelviewModel = ViewModelProviders.of(this).get(UserViewModel.class);
Causée par: Java.lang.RuntimeException: impossible de créer une instance de la classe UserViewModel Causée par: Java.lang.InstantiationException: Java.lang.Class n'a pas de constructeur zéro argument
Lors de l'initialisation des sous-classes de ViewModel
à l'aide de ViewModelProviders
, il s'attend par défaut à ce que votre classe UserModel
ait un constructeur d'argument nul. Dans votre cas, votre constructeur a l'argument MutableLiveData<User> user
Une façon de résoudre ce problème consiste à avoir un constructeur sans argument par défaut pour votre UserModel
Sinon, si vous souhaitez avoir un constructeur à argument non nul pour votre classe ViewModel, vous devrez peut-être créer une classe ViewModelFactory
personnalisée pour initialiser votre instance ViewModel, qui implémentera l'interface ViewModelProvider.Factory
.
Je n'ai pas encore essayé cela, mais voici le lien vers l'excellent exemple de Google pour le même: github.com/googlesamples/Android-architecture-components . Plus précisément, consultez cette classe GithubViewModelFactory.Java pour le code Java et cette classe GithubViewModelFactory.kt pour le code Kotlin correspondant
ViewModelFactory
qui nous fournira un bon ViewModel de ViewModelModule
public class ViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModels;
@Inject
public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModels) {
this.viewModels = viewModels;
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
Provider<ViewModel> viewModelProvider = viewModels.get(modelClass);
if (viewModelProvider == null) {
throw new IllegalArgumentException("model class " + modelClass + " not found");
}
return (T) viewModelProvider.get();
}
}
ViewModelModule
est responsable de la liaison de toutes les classes ViewModel dans Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModels
@Module
public abstract class ViewModelModule {
@Binds
abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory viewModelFactory);
//You are able to declare ViewModelProvider.Factory dependency in another module. For example in ApplicationModule.
@Binds
@IntoMap
@ViewModelKey(UserViewModel.class)
abstract ViewModel userViewModel(UserViewModel userViewModel);
//Others ViewModels
}
ViewModelKey
est une annotation à utiliser comme clé dans la carte et ressemble à
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@MapKey
@interface ViewModelKey {
Class<? extends ViewModel> value();
}
Vous pouvez maintenant créer ViewModel et satisfaire toutes les dépendances nécessaires à partir du graphique.
public class UserViewModel extends ViewModel {
private UserFacade userFacade;
@Inject
public UserViewModel(UserFacade userFacade) { // UserFacade should be defined in one of dagger modules
this.userFacade = userFacade;
}
}
Instanciation de ViewModel
public class MainActivity extends AppCompatActivity {
@Inject
ViewModelFactory viewModelFactory;
UserViewModel userViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((App) getApplication()).getAppComponent().inject(this);
userViewModel = ViewModelProviders.of(this, viewModelFactory).get(UserViewModel.class);
}
}
Et n'oubliez pas d'ajouter ViewModelModule
dans la liste modules
@Singleton
@Component(modules = {ApplicationModule.class, ViewModelModule.class})
public interface ApplicationComponent {
//
}
Le problème peut être résolu en développant UserModel
à partir de AndroidViewModel
, qui est ViewModel sensible au contexte de l'application et qui nécessite le constructeur Application
avec paramètre uniquement. (Documentation)
Ex- (en kotlin)
class MyVm(application: Application) : AndroidViewModel(application)
Cela fonctionne pour la version 2.0.0-alpha1
.
J'ai écrit une bibliothèque qui devrait rendre la réalisation de cette tâche plus simple et plus nette, sans multibindings ni embase standard, tout en donnant la possibilité de paramétrer davantage la ViewModel
à l'exécution: https://github.com/radutopor/ ViewModelFactory
@ViewModelFactory
class UserViewModel(@Provided repository: Repository, userId: Int) : ViewModel() {
val greeting = MutableLiveData<String>()
init {
val user = repository.getUser(userId)
greeting.value = "Hello, $user.name"
}
}
Dans la vue:
class UserActivity : AppCompatActivity() {
@Inject
lateinit var userViewModelFactory2: UserViewModelFactory2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
appComponent.inject(this)
val userId = intent.getIntExtra("USER_ID", -1)
val viewModel = ViewModelProviders.of(this, userViewModelFactory2.create(userId))
.get(UserViewModel::class.Java)
viewModel.greeting.observe(this, Observer { greetingText ->
greetingTextView.text = greetingText
})
}
}