Jetpack ComposeにViewModelをDIする方法として2通り見つけた。
androidx.hilt.navigation.compose.hiltViewModel
とandroidx.lifecycle.viewmodel.compose.viewModel
ググって出てきた記事ではhiltViewModel
を使っているケースが多い気がしたがこれはnavigation.compose
の中にあるし、公式にはviewmodel.compose.viewModel
を使っているパターンが紹介されている(*) しで、どちらがいいのかわからないのでちょっと調べてみた。
hiltViewModel
コード中のコメントには以下のように書かれていて既存のHiltViewModelがあればそれを返してくれるのかな??
/* * Returns an existing * HiltViewModel * -annotated [ViewModel] or creates a new one scoped to the current navigation graph present on * the {@link NavController} back stack. * * If no navigation graph is currently present then the current scope will be used, usually, a * fragment or an activity. * * @sample androidx.hilt.navigation.compose.samples.NavComposable * @sample androidx.hilt.navigation.compose.samples.NestedNavComposable /
実装
実装はこんな風になっててViewModelStoreOwner
が気になる。
checkNotNullでLocalViewModelStoreOwner.current
が存在しないと例外を投げるみたいだ。
if (viewModelStoreOwner is NavBackStackEntry)
を見ているので、バックスタックに存在していればそれを返してくれるイメージなのかな。
@Composable inline fun <reified VM : ViewModel> hiltViewModel( viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) { "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner" } ): VM { val factory = createHiltViewModelFactory(viewModelStoreOwner) return viewModel(viewModelStoreOwner, factory = factory) } @Composable @PublishedApi internal fun createHiltViewModelFactory( viewModelStoreOwner: ViewModelStoreOwner ): ViewModelProvider.Factory? = if (viewModelStoreOwner is NavBackStackEntry) { HiltViewModelFactory( context = LocalContext.current, navBackStackEntry = viewModelStoreOwner ) } else { // Use the default factory provided by the ViewModelStoreOwner // and assume it is an @AndroidEntryPoint annotated fragment or activity null }
viewModel
コード中のコメントには以下のように書かれていてこれも既存のものがあれば返してくれるようなことが書いてある
Returns an existing ViewModel or creates a new one in the given owner (usually, a fragment or an activity), defaulting to the owner provided by LocalViewModelStoreOwner. The created ViewModel is associated with the given viewModelStoreOwner and will be retained as long as the owner is alive (e.g. if it is an activity, until it is finished or process is killed). Params: viewModelStoreOwner - The owner of the ViewModel that controls the scope and lifetime of the returned ViewModel. Defaults to using LocalViewModelStoreOwner. key - The key to use to identify the ViewModel. factory - The ViewModelProvider.Factory that should be used to create the ViewModel or null if you would like to use the default factory from the LocalViewModelStoreOwner Returns: A ViewModel that is an instance of the given VM type.
実装
こっちもViewModelStoreOwner
がいる。
こっちはバックスタックとか関係なくViewModelStoreOwner
に紐づいるものが存在していればそれを返してくれるイメージ??
@Suppress("MissingJvmstatic") @Composable public inline fun <reified VM : ViewModel> viewModel( viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) { "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner" }, key: String? = null, factory: ViewModelProvider.Factory? = null ): VM = viewModel(VM::class.java, viewModelStoreOwner, key, factory) @Suppress("MissingJvmstatic") @Composable public fun <VM : ViewModel> viewModel( modelClass: Class<VM>, viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) { "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner" }, key: String? = null, factory: ViewModelProvider.Factory? = null ): VM = viewModelStoreOwner.get(modelClass, key, factory) private fun <VM : ViewModel> ViewModelStoreOwner.get( javaClass: Class<VM>, key: String? = null, factory: ViewModelProvider.Factory? = null ): VM { val provider = if (factory != null) { ViewModelProvider(this, factory) } else { ViewModelProvider(this) } return if (key != null) { provider.get(key, javaClass) } else { provider.get(javaClass) } }
使い分けイメージ
- 画面単位で同じViewModelのインスタンスを返して欲しいなら
hiltViewModel
- ことなる画面でも同じViewModelのインスタンスを共有したいなら
viewModel
という理解でいいんだろうか。
もう少し余裕があればもっと深く調べてみたい。
ハマりポイント
hilt-composeライブラリをimplementationしたらRecords requires ASM8
というエラーが出た
詳細を見るとFailed to transform moshi-1.13.0.jar
とのこと
// When using Kotlin. kapt "androidx.hilt:hilt-compiler:1.0.0" implementation "androidx.hilt:hilt-navigation-compose:1.0.0-rc01" implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0"
ググるとDaggerにIssueが載っていて解決法として下記を発見。 https://github.com/square/moshi/issues/1463#issuecomment-994576201
正しいのかわからないが、ワークアラウンドとして gradle.propertiesに下記を追記すると解決。
android.jetifier.ignorelist=moshi-1.13.0
* https://developer.android.com/jetpack/compose/libraries#hilt