Help us understand the problem. What is going on with this article?

Activity, Fragmentを跨いでViewModelを共有する

はじめに

みなさんこんにちは。AndroidのViewModel, 便利ですよね。
Activityが再生成されるときのデータの復元をシンプルにできますが、私は次のようなことがやりたいと思いました。

やりたいこと

スコープの異なるActivity同士やActivity, Fragment間でViewModelを共有したい

Activity間でViewModelを共有したい.001.jpeg

図のようにFirstActivityとSecondActivityとで同じViewModelを共有できたらいいなと思う場面がありました。
具体的には、写真のビューアーで以下のようにGridな表示とListな表示切り替えられる機能です。

具体例.002.png

これら2つの画面では同じImageのListを保持しているし、その写真の情報も同じなため、同じViewModelを共有したいと思いました。

ViewModelの共有について

Fragment間でのViewModelの共有はサポートされています。
https://developer.android.com/topic/libraries/architecture/viewmodel?hl=JA#sharing
なぜならActivityが共有なため、スコープが共通だから。(言い方があっているかは不安)

今回、私の場合はFragmentとActivity間でやり取りさせたいため, このやり方ではうまくできませんでした。

ViewModelStoreOwner

そこで、ViewModelStoreOwnerの出番です。
ViewModelStoreOwner は、ViewModelを保存しておくViewModelStoreのスコープを定義することができます。

ViewModelStoreOwnerは抽象メソッドをひとつだけもつinterfaceです。

public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}

流れとしては、ViewModelを共有したいActivityに、ViewModelStoreOwnerを実装して、共通のViewModelStoreを使えるようにできればと思いました。

まずは同じViewModelStoreが取得できるよう、ViewModelStoreOwnerにデフォルト実装を加えたinterfaceを用意しました。

interface PhotoViewModelStoreOwner : ViewModelStoreOwner {
     override fun getViewModelStore(): ViewModelStore = // SingletonなViewModelStoreをDIなどで挿入
}

これをViewModelを共有したいActivity, Fragmentに実装します。

class PhotoGridFragment : Fragment(), SnapPhotoViewModelStoreOwner {
     private val viewModel: PhotoViewModel by lazy {
        PhotoViewModel.get(id, photoTag, this)
     }
     override fun getViewModelStore(): ViewModelStore {
        return super<PhotoViewModelStoreOwner>.getViewModelStore()
     }
}
class PhotoListActivity : AppCompatActivity(), SnapPhotoViewModelStoreOwner {
     private val viewModel: PhotoViewModel by lazy {
        PhotoViewModel.get(id, photoTag, this)
     }
     override fun getViewModelStore(): ViewModelStore {
        return super<PhotoViewModelStoreOwner>.getViewModelStore()
     }
}

そして、ファクトリーメソッドをViewModel側に用意しました。

companion object {
        fun get(
            id: Int,
            photoTag: String?,
            viewModelStoreOwner: SnapPhotoViewModelStoreOwner
        ): PhotoViewModel = ViewModelProvider(viewModelStoreOwner, PhotoViewModelFactory(id, photoTag))
                .get(photoTag ?: "all", PhotoViewModel::class.java)
}

これで、ViewModelを共有することができます。
なお、ViewModelStoreは明示的にclearしないとViewModelが溜まってしまうため、スコープの長い方のActivity or Fragment のon Destoryでclearするといいでしょう。

override fun onDestroy() {
        viewModelStore.clear()
        super.onDestroy()
}

おまけ

ActivityScopeなViewModelでは、スコープの違う者同士共有できませんが、ApplicationScopeなら、Activityが破棄された後も生き残るため、共有することができます。
やり方は、ViewModelStoreOwnerの実装を、Applicationにするだけです。

class MyApplication : Application, ViewModelStoreOwner {
    private val viewModelStore : ViewModelStore = ViewModelStore()
    override fun getViewModelStore(): ViewModelStore = viewModelStore
}

しかし、このやり方だとclearができず、ViewModelがActivityがいなくなった後も永遠と残ってしまうため、いいやり方ではありません。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away