by ViewModels() を使って ViewModel を取得する方法
以前は以下のように ViewModelProviders を使用して ViewModel を取得していましたが、この方法は Deprecated になりました。
1 | val myViewModel = ViewModelProviders.of( this ). get (MyViewModel:: class .java) |
ドキュメントを見てみると、代わりに kotlin の委譲 by viewModels()
、もしくは ViewModelProvider
1 を使ってね。と書いてあります。
ところが、by viewModels()
はどう使うのかが、何処にも書いてありません2。
そこで、ネットの情報とコードを参考に使ってみました。
依存関係
viewModels
は fragment-ktx
というパッケージ内に入っているので3、これを implementation
に加えます。
1 2 3 4 | dependencies { ... implementation 'androidx.fragment:fragment-ktx:1.2.2' } |
コンパイラのバージョンを 1.8 互換に
環境によっては以下のように、JVM のターゲットバージョンが合ってない旨のエラーがでてしまいます。
"Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6. Please specify proper '-jvm-target' option"
これを解決するには、以下のように build.gradle
に追記します。
1 2 3 4 5 6 7 8 9 10 11 | android { ... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = JavaVersion.VERSION_1_8.toString() } } |
この問題、viewModels()
に限ったことではなく、新しめの kotlin ライブラリを使おうとすると起こります。
参考
基本の使い方
以下のように使用します。
1 2 3 4 5 6 7 8 9 10 | class MainActivity : AppCompatActivity() { // lazy なので最初に使用する時に初期化される private val myViewModel by viewModels<MyViewModel>() fun something() { myViewModel.users.observe( this , Observer { // Do something }) } } |
Factory 指定
Factory を指定した初期化の方法は以下。
1 | private val myViewModel by viewModels<MyViewModel>{ MyViewModelFactory() } |
viewModels
の実装を覗いてみると、以下のようになっています。
Factory
を返す関数を与えられるようになっているわけですね。
1 2 3 4 5 6 7 8 9 10 | @MainThread inline fun < reified VM : ViewModel> ComponentActivity.viewModels( noinline factoryProducer: (() -> Factory)? = null ): Lazy<VM> { val factoryPromise = factoryProducer ?: { defaultViewModelProviderFactory } return ViewModelLazy(VM:: class , { viewModelStore }, factoryPromise) } |
ViewModelLazy()
も public なクラスなので、これを直接使えば ViewModelStoreOwner
(Activity や Fragment 等)を指定できます。
しかし、これは遅延初期化オブジェクト。どのタイミングで呼ばれるかが定かではありません4。
Activity や Fragment 等、ライフサイクルによっては正しく動作しないものを指定すべきではありません。
そういった用途には以下の ViewModelProvider
を使うのが良いでしょう。
【参考】ViewModelProvider クラスを使う
ドキュメントには by viewModels()
もしくは ViewModelProvider() を使って。とかいてあります。
こちらは、ViewModelProviders.of()
とほぼ同様なので、簡単に使えるかと思います。
1 2 | // ViewModelProviders.of(this) とほぼ同じように使える val myViewModel= ViewModelProvider( this ). get (MyViewModel:: class .java) |
0 件のコメント:
コメントを投稿