studio livedata example dependency androidviewmodel android mvvm architecture-components

livedata - Android ViewModel no tiene constructor argumento cero



viewmodel android (5)

Al inicializar las subclases de ViewModel usando ViewModelProviders , por defecto, espera que su clase UserModel tenga cero constructor de argumentos. En su caso, su constructor tiene argumento MutableLiveData<User> user

Una forma de solucionar este problema es tener un constructor de UserModel no predeterminado para su UserModel

De lo contrario, si desea tener un constructor de argumento distinto de cero para su clase ViewModel, es posible que deba crear una clase personalizada de ViewModelFactory para inicializar su instancia de ViewModel, que implementará la interfaz ViewModelProvider.Factory .

No he intentado esto todavía, pero aquí está el enlace a una excelente muestra de google para el mismo: github.com/googlesamples/android-architecture-components . Específicamente, GithubViewModelFactory.java esta clase GithubViewModelFactory.java para el código Java y esta clase GithubViewModelFactory.kt para el código Kotlin correspondiente

Estoy siguiendo this documentación para aprender sobre LiveData y ViewModel. En el documento, la clase ViewModel tiene un constructor como tal,

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; } }

Sin embargo, cuando ejecuto el código, obtengo una excepción:

final UserViewModelviewModel = ViewModelProviders.of(this).get(UserViewModel.class);

Causado por: java.lang.RuntimeException: No se puede crear una instancia de la clase UserViewModel Causado por: java.lang.InstantiationException: java.lang.Class no tiene un constructor de argumento cero


El problema se puede resolver extendiendo UserModel desde AndroidViewModel que es compatible con el contexto de la aplicación ViewModel y requiere un constructor de parámetros de Application solamente. (documentation)

Ex- (en kotlin)

class MyVm(application: Application) : AndroidViewModel(application)

Esto funciona para la versión 2.0.0-alpha1 .


Escribí una biblioteca que debería hacer que lograr esto sea más sencillo y limpio, sin necesidad de multibindings o boilerplate de fábrica, al mismo tiempo que le ViewModel la posibilidad de parametrizar aún más el ViewModel en tiempo de ejecución: 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" } }

En la vista:

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 }) } }


Si tienes parámetro en el constructor entonces:

DAGGER 2 constructor público para la dependencia @inject

@Inject public UserViewModel(UserFacade userFacade) { this.userFacade = userFacade; }

De lo contrario, la daga 2 le enviará el error "no se puede instanciar el objeto de modelo de vista"


ViewModelFactory que nos proporcionará un ViewModel correcto 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 es responsable de vincular todas las clases de ViewModel a
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 es una anotación para usar como clave en el mapa y se ve así

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @MapKey @interface ViewModelKey { Class<? extends ViewModel> value(); }

Ahora puede crear ViewModel y satisfacer todas las dependencias necesarias del gráfico

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; } }

Instalando 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); } }

Y no ViewModelModule agregar ViewModelModule en la lista de modules

@Singleton @Component(modules = {ApplicationModule.class, ViewModelModule.class}) public interface ApplicationComponent { // }