概要
AndroidでViewModelに保持したLiveDataの値をアプリケーションで共有したい場合のメモ
方針
本来の仕様では起動中のActivityのインスタンスとViewModelが1対1で対応するため、
Activityのインスタンスが変わってしまったり、異なるActivityのインスタンスからLiveDataをobserveしても、
変更検知ができません
class MyActivity : AppCompatActivity() {
private val userViewModel: UserViewModel by lazy { ViewModelProviders.of(this).get(MyViewModel::class.java) }
override fun onCreate(savedInstanceState: Bundle?) {
userViewModel.user.observe(this, Observer<User>{ user ->
// OtherActivityでの変更は検知できない
})
}
}
class OtherActivity : AppCompatActivity() {
private val userViewModel: UserViewModel by lazy { ViewModelProviders.of(this).get(MyViewModel::class.java) }
override fun onCreate(savedInstanceState: Bundle?) {
userViewModel.user.observe(this, Observer<User>{ user ->
// 変更検知できる
})
// データの更新を実行
userViewModel.fetch()
}
}
ViewModelProvider.Factory
を継承したクラスを作成し、
ViewModelのクラスの名前で ViewModelProviders
から取得するViewModelが
一意になるようにすることでアプリケーション内でLievDataを共有できるようにしました。
実装したコード
ViewModelProvider.Factory
を継承した ViewModelFactory
というクラスを作成
class ViewModelFactory: ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (ViewModel::class.java.isAssignableFrom(modelClass)) {
val key = modelClass.toString()
return if(hashMapViewModel.containsKey(key)){
getViewModel(key) as T
} else {
val vm: T?
try {
vm = modelClass.newInstance()
} catch (e: IllegalAccessException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: InstantiationException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: InvocationTargetException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
}
addViewModel(key, vm)
getViewModel(key) as T
}
}
throw IllegalArgumentException("Unknown ViewModel class")
}
companion object {
val hashMapViewModel = HashMap<String, ViewModel>()
fun addViewModel(key: String, viewModel: ViewModel?){
viewModel?.let { hashMapViewModel.put(key, it)
}
}
fun getViewModel(key: String): ViewModel? {
return hashMapViewModel[key]
}
}
}
使い方
LiveDataをアプリケーション内で共有したいときのみ、
下記のようにViewModel生成時に ViewModelFactory
を渡してあげればOK
class MyActivity : AppCompatActivity() {
private val viewModelFactory: ViewModelFactory = ViewModelFactory()
private val userViewModel: UserViewModel by lazy { ViewModelProviders.of(this, viewModelFactory).get(MyViewModel::class.java) }
override fun onCreate(savedInstanceState: Bundle?) {
userViewModel.users.observe(this, Observer<List<User>>{ users ->
// LiveDataがアプリケーション全体から参照できるようになる
})
}
}