0
1

More than 1 year has passed since last update.

ViewModelProvider.Factory を省略する方法

Last updated at Posted at 2021-11-03

■ 概要

ViewModel にコンストラクタ引数が存在する場合に ViewModelProvider.Factory の実装を毎回作るのがめんどくさいので、それを省略する。

■ Before

こういうのがめんどくさい

// Factory を呼ぶのがめんどい。
// Lazy で受け取る必要性がない。
val viewModel by viewModels<XxxActivityViewModel> { Factory(arg1) }

// Factory を実装するのがめんどい。
class Factory(private val arg1: String) : ViewModelProvider.Factory {
    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel?> create(modelClass: Class<T>): T =
        XxxActivityViewModel(arg1) as T
}

■ After

こんな感じでらくちん。

val viewModel = viewModels(::XxxActivityViewModel, arg1)

■ ライブラリ的なもの

import androidx.activity.ComponentActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.Factory
import kotlin.reflect.KFunction1

// コンストラクタ引数が1つの場合の関数。
// - 静的な型チェックをしたほうがいいと思うので、引数分だけ関数を作るのがよさげ。
// - ()->Unit の lambda 1 つで済ませる方法のほうが万能だが、おいら的には微妙。
//   lamda の括弧が目に入った瞬間に脳のリソースが多めに消費されるような気がする。
// - 以前は Lazy<VM> を返すものを作ってみたが、そもそもライフサイクルのある Activity や
//   Fragment の member として配置するようなバギーなことをさせないためにも、あえて現物を返すようにした。
@Suppress("UNCHECKED_CAST")
inline fun <reified VM : ViewModel, A1> ComponentActivity.viewModels(factoryFunction: KFunction1<A1, VM>, arg1: A1): VM {
    val factory = object : Factory {
        override fun <VM : ViewModel?> create(modelClass: Class<VM>): VM = factoryFunction(arg1) as VM
    }

    return ViewModelProvider(viewModelStore, factory).get(VM::class.java)
}

■ まとめ

  • ViewModel のコンストラクタ引数が存在する場合で、ViewModelProvider.Factory の実装が単純にコンストラクタを呼んでいるだけなのであれば、こんな感じでやっても良さそうな気がする。
  • Lazy で返せるようにしておくと、Activity の member として val viewModel のように定義される可能性が増す。onCreate() 内部で初めてアクセスされる分には問題なく動作するが、別の member 初期化時に viewModel を取得してしまったりして onCreate() 以外の箇所で ViewModel の instance が生成されるようなことが起き得る。また、intent の情報等をコンストラクタに渡す必要があるような場合にコンパイルエラーとして検出できないバグになるようなこともある。
  • 引数を ()-> Unit とすれば lambda 1 つで済ませられる1が、おいら的には微妙。lamda の括弧が目に入った瞬間に脳のリソースが多めに消費されるような気がする2
  • vararg と reflection を使って引数を可変にすることもできるけど、静的に型チェックできたほうが保守性が高いと思うので、引数分だけ関数を作るのが無難な気がしている今日この頃。

ということで、おしまい。


  1. どのようなファクトリであろうが関数1個で対応できるので。 

  2. 純粋においらの主観です。 

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1