

sharedViewModelを扱いたい。そのVMはコンストラクタで引数を受け取る。  
これviewModelsとかactivityViewModelsでどうかけばいいでしょうか？

[code]

    class MyViewModel(val a: Int): ViewModel() {
      class Factory constructor(private val a: Int): ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
                @Suppress("UNCHECKED_CAST")
                return MyViewModel(this.a) as T
        }
      }
    }
    
    class MyActivity: AppCompatActivity() {
      // viewModels is fragment-ktx package
      val viewModel: MyViewModel by viewModels { MyViewModel.Factory(a = 1) }
    }
    
    class MyFragment: Fragment() {
        val viewModel: MyViewModel by activityViewModels() // raise Cannot create an instance of class com.example.myapp.MyViewModel
    }
    
[/code]


CC BY-SA 4.0
2020-10-27T13:04:43.793
71540
2022-07-25T07:01:30.283
2020-10-28T03:20:14.063
15413
427
question
android kotlin
androidで、コンストラクタに引数があるVMを、ActivityとFragment間で共有する方法


## 原因

<https://developer.android.com/reference/kotlin/androidx/fragment/app/package-
summary?hl=ja#(androidx.fragment.app.Fragment).activityViewModels(kotlin.Function0)>

> Returns a property delegate to access parent activity's ViewModel, if
> factoryProducer is specified then ViewModelProvider.Factory returned by it
> will be used to create ViewModel first time. Otherwise, the activity's
> androidx.activity.ComponentActivity.getDefaultViewModelProviderFactory will
> be used.

google翻訳

>
> 親アクティビティのViewModelにアクセスするためのプロパティデリゲートを返します。factoryProducerが指定されている場合、それによって返されるViewModelProvider.Factoryは、ViewModelの最初の作成に使用されます。
> それ以外の場合は、アクティビティのandroidx.activity.ComponentActivity.getDefaultViewModelProviderFactoryが使用されます。

とあるので、エラーの原因は、以下のナノではないかと推察します

  * ActivityのViewModel取得よりもFragmentのViewModel取得が先にはしった
  * ActivityにはまだViewModelが取得されていないため、FragmentのactivityViewModelsはviewModelの最初の作成の処理が走る
  * FragmentのactivityViewModelsは引数が無いため親ActivityのgetDefaultViewModelProviderFacyoryを使ってViewModelを生成しようとする
  * 親ActivityのgetDefaultViewModelProviderFacyoryはデフォルトのままで、カスタムFactoryではない(viewModelsを使っているからと言ってdefaultViewModelProviderFactoryが変わったりはしていない模様)
  * デフォルトのViewModelProviderFacyoryは引数のあるViewModel::classが指定された場合にinstance生成に失敗する

## 対策

ActivityのetDefaultViewModelProviderFacyoryをoverrideしてcustomのFactoryをreturnするように変更する

[code]

    class MyViewModel(val a: Int): ViewModel() {
      class Factory constructor(private val a: Int): ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
                @Suppress("UNCHECKED_CAST")
                return MyViewModel(this.a) as T
        }
      }
    }
    
    class MyActivity: AppCompatActivity() {
      // viewModels is fragment-ktx package
    -  val viewModel: MyViewModel by viewModels { MyViewModel.Factory(a = 1) }
    +  val viewModel: MyViewModel by viewModels()
    
    +  override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory {
    +    return MyViewModel.Factory(a = 1)
    +  }
    }
    
    class MyFragment: Fragment() {
        val viewModel: MyViewModel by activityViewModels() // 成功！ そしてshareされている!
    
    }
    
[/code]


CC BY-SA 4.0
2020-10-28T05:14:47.803
71555
2020-10-28T05:14:47.803
427
71540
answer