LiveDataとViewModelについて学ぶために this ドキュメンテーションに従っています。ドキュメントでは、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にゼロ引数コンストラクターがありません
ViewModel
を使用してViewModelProviders
のサブクラスを初期化するとき、デフォルトでは、UserModel
クラスにゼロ引数コンストラクターがあると想定されます。あなたの場合、コンストラクタには引数MutableLiveData<User> user
があります
これを修正する1つの方法は、UserModel
のデフォルトの引数なしコンストラクタを使用することです
それ以外の場合、ViewModelクラスにゼロ以外の引数コンストラクターが必要な場合、カスタムViewModelFactory
クラスを作成して、ViewModelインスタンスを初期化し、ViewModelProvider.Factory
インターフェイスを実装する必要があります。
私はまだこれを試していませんが、同じもののためのグーグルからの優秀なサンプルへのリンクはここにあります: github.com/googlesamples/Android-architecture-components 。具体的には、このクラスをチェックアウト GithubViewModelFactory.Java Javaコードおよびこのクラス GithubViewModelFactory.kt 対応するKotlinコード
ViewModelFactory
は、ViewModelModule
から適切なViewModelを提供します
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 {
//
}
私は、実行時にViewModel
をさらにパラメーター化する機能を提供しながら、これをより簡単で、よりクリーンで、マルチバインディングやファクトリーボイラープレートを必要としないライブラリを作成しました。 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"
}
}
ビューで:
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
})
}
}
この問題は、UserModel
をアプリケーションコンテキスト対応のViewModelであり、AndroidViewModel
パラメーターのみのコンストラクターを必要とするApplication
を拡張することで解決できます。 (ドキュメント)
Ex-(コトリン)
class MyVm(application: Application) : AndroidViewModel(application)
これはバージョン2.0.0-alpha1
で機能します。