Android ViewModel не имеет конструктора нулевых аргументов
Я следую этой документации, чтобы узнать о LiveData и ViewModel.
В документе doc класс 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;
}
}
Однако, когда я запускаю код, я получаю исключение:
final UserViewModelviewModel = ViewModelProviders.of(this).get(UserViewModel.class);
Вызвано: java.lang.RuntimeException: невозможно создать экземпляр класса UserViewModel Вызывается: java.lang.InstantiationException: java.lang.Class не имеет конструктора нулевых аргументов
Ответы
Ответ 1
При инициализации подклассов ViewModel
с помощью ViewModelProviders
по умолчанию он ожидает, что ваш класс UserModel
будет иметь нулевой конструктор аргументов. В вашем случае у вашего конструктора есть аргумент MutableLiveData<User> user
Один из способов исправить это - создать конструктор arg по умолчанию для вашего UserModel
В противном случае, если вы хотите иметь конструктор ненулевых аргументов для вашего класса ViewModel, вам может потребоваться создать собственный класс ViewModelFactory
для инициализации экземпляра ViewModel, который будет реализовывать интерфейс ViewModelProvider.Factory
.
Я еще не пробовал, но вот ссылка на отличный образец из Google для этого же: github.com/googlesamples/android-architecture-components. В частности, проверьте этот класс GithubViewModelFactory.java для Java-кода и этот класс GithubViewModelFactory.kt для соответствующего кода Котлина
Ответ 2
ViewModelFactory
, который предоставит нам правую ViewModel из 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
отвечает за привязку всех классов ViewModel к
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
- это аннотация для использования в качестве ключа на Карте и выглядит
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@MapKey
@interface ViewModelKey {
Class<? extends ViewModel> value();
}
Теперь вы можете создавать ViewModel и удовлетворять всем необходимым зависимостям от графика
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;
}
}
Создание экземпляра 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);
}
}
И не подделывать, чтобы добавить ViewModelModule
в список modules
@Singleton
@Component(modules = {ApplicationModule.class, ViewModelModule.class})
public interface ApplicationComponent {
//
}
Ответ 3
Если у вас есть параметр в конструкторе, тогда:
Общий конструктор DAGGER 2 для зависимостей @inject
@Inject
public UserViewModel(UserFacade userFacade)
{
this.userFacade = userFacade;
}
В противном случае кинжал 2 отправит вам сообщение об ошибке "невозможно создать экземпляр объекта viewmodel"
Ответ 4
Проблема может быть решена путем расширения UserModel
из AndroidViewModel
который является ViewModel, ориентированным на контекст приложения, и требует конструктор только для параметров Application
. (документация)
Ex- (в котлине)
class MyVm(application: Application) : AndroidViewModel(application)
Это работает для версии 2.0.0-alpha1
.