はじめに
気軽に Activity 間でデータを共有する場合は、Application を継承したクラスを ViewModelStoreOwner にすると良いです。このように設計することで、Application を継承したクラスが肥大化することなく、Application にグローバルな値を持たせることができます。
実装
class MyApplication : Application(), ViewModelStoreOwner {
private val store = ViewModelStore()
override fun getViewModelStore(): ViewModelStore {
return store
}
}
Application の実装はこれだけです。今後グローバルな状態が増えたとしても、Application が肥大化することはありません。代わりに ViewModel の数が増えます。
使用例
次のコードは、SubActivity
で加算した値が MainActivity
にすぐに反映されるコードです。Jetpack Compose を使用しています。
class MyViewModel : ViewModel() {
var count by mutableStateOf(0)
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ViewModel の実体を取得するコード。このコードは SubActivity でも使われる
val vm = ViewModelProvider(application as MyApplication).get(MyViewModel::class.java)
setContent {
MainScreen(vm, ::move)
}
}
private fun move() {
val intent = Intent(this, SubActivity::class.java)
startActivity(intent)
}
}
@Composable
fun MainScreen(vm: MyViewModel, onClick: () -> Unit) {
Button(onClick = onClick) {
Text("click me! ${vm.count}")
}
}
class SubActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm = ViewModelProvider(application as MyApplication).get(MyViewModel::class.java)
setContent {
SubScreen(vm)
}
}
}
@Composable
fun SubScreen(vm: MyViewModel) {
Button(onClick = { vm.count += 1 }) {
Text("click add! ${vm.count}")
}
}
おわりに
この実装パターンを使うことで、いいね問題が解決すると思います。
余談
次のようにシングルトンオブジェクトを作れば、Application を継承したクラスを作る必要すらありません。
object MyData {
var count by mutableStateOf(0)
}
このオブジェクトを使うようにしたとき、MainActivity と SubActivity のコードにある val vm = ViewModelProvider(application as MyApplication).get(MyViewModel::class.java)
がなくなります。もし Application とシングルトンオブジェクトの生存期間が同じなのであれば、こちらでもいい気がします。ただ、Composable の依存する状態が引数として表れないのは少し良くないかもしれないです。