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.