【Android】ActivityとFragmentでのViewModelの持たせ方

Android開発でActivityやFragmentにViewModelを実装することは非常に大きなメリットがあります。

一方で、FragmentでのViewModelは基本的には各Fragmentで独立した個別のものとなるため、例えば異なるFragmentのViewModel間でデータのやり取りを行いたい場合などは少しだけ工夫が必要です。

ViewModelの持たせ方・設計のやり方は人やプロジェクトの性質などによって様々ですが、個人的に行っているやり方をメモとして以下に記載します。


ViewModelの基本的な実装方針
  1. 一つのActivity配下で共有したいデータはメインActivityのViewModelに実装

  2. 同一Activity配下の特定の子Fragment間でやり取りが必要な場合は、必要なFragmentに親ActivityをオーナーとしたViewModelを実装して共有

  3. ActivityによらずにFragmentでやり取りを行いたい場合は、独立したViewModelオーナーを実装して共有

1. 一つのActivity配下でのデータ共有

これに関しては特に難しい事はなく、単にActivityに実装したViewModelを子Fragmentから呼び出すだけです。

lateinit var mainActivityViewModel: SomeMainActivityViewModel
    private set

override fun onCreatea(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    mainActivityViewModel = ViewModelProvider(this)[SomeMainActivityViewModel::class.java]
}

// アプリで実装するActivityが一つだけの場合は、以下のようなviewModels拡張の実装でも不都合はない
// val mainActivityViewModel: SomeMainActivityViewModel by viewModels()
// 子Fragmentからは、親Activityと実装されているViewModelの型を参照して呼び出す

lateinit var parentActivityViewModel: SomeMainActivityViewModel
    private set

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    parentActivityViewModel = ViewModelProvider(requireActivity())[SomeMainActivityViewModel::class.java]
}

// 以下の実装でもOKだが、Fragmentを高速に切り替えた時などに正しく参照できない可能性あり
// var parentActivityViewModel: SomeMainActivityViewModel by activityViewModels()

尚、上記の例では、拡張として定義されている「by viewModels()」などを用いるのを主体にはしていませんが、アプリの動作によってはviewModels()拡張だとViewModelが正しく参照されないケースもあるため、少し長くはなるものの確実な方法をとっています(他の記事を参照のこと)。これ以降での実装例も同様です。


2. 同一Activity配下での特定のFragment間での共有

こちらは上記の1と似ていて、Fragment間で共有したいViewModelを親Activityをオーナーにして呼び出します。

// 以下の実装を共有したいFragmentにそれぞれ実装することでViewModelによるデータ共有が可能となる
// "by activityViewModels()"での実装でも不都合が無ければOK

lateinit var shareViewModel: SomeShareViewModel
    private set

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    shareViewModel = ViewModelProvider(requireActivity())[SomeShareViewModel::class.java]
}

3. ActivityによらずにFragment間でViewModelを共有

Activityによらず自由にFragment間でViewModelを共有したい場合は、独自にViewModelのオーナーを設ける必要があります。
が、特にこれも難しい実装ではなく、ViewModelStoreOwnerインターフェイスを実装したクラスのインスタンスをViewModelのオーナーとして設定し呼び出す実装にすれば実現できます。
注意点としては、オーナーのインタンスは同一でないとダメなので、Singletonなどで同一のインスタンスを保持しておく実装が必要です。

class SampleViewModelOwner private constructor() : ViewModelStoreOwner {

    companion object {
        val instance = SampleViewModelOwner()
    }

    private val vmStore = ViewModelStore()

    override fun getViewModelStore(): ViewModelStore {
        return vmStore
    }
}
// 以下を必要なFragmentに実装

lateinit var globalShareViewModel: SomeGlobalShareViewModel
    private set

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    globalShareViewModel = ViewModelProvider(SampleViewModelOwner.instance)[SomeGlobalShareViewModel::class.java]
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です