【问题标题】:android ViewModelFactory with hilt带刀柄的 android ViewModelFactory
【发布时间】:2020-10-20 08:39:21
【问题描述】:

我首先尝试安卓ViewModelHiltDI

据我从下面的链接了解,要在运行时使用值初始化 ViewModel,我应该使用 ViewModelFactory

Use a ViewModelFactory

//ViewModel
class ScoreViewModel(finalScore: Int) : ViewModel() {
   // The final score
   var score = finalScore
   init {
       Log.i("ScoreViewModel", "Final score is $finalScore")
   }
}

//ViewModelFactory
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
   if (modelClass.isAssignableFrom(ScoreViewModel::class.java)) {
       return ScoreViewModel(finalScore) as T
   }
   throw IllegalArgumentException("Unknown ViewModel class")
}


//Fragment
viewModelFactory = ScoreViewModelFactory(ScoreFragmentArgs.fromBundle(arguments!!).score)

要使用带有刀柄的 ViewModel,我应该使用@ViewModelInject,如下面的链接所述

Hilt and Jetpack integrations

//ViewModel
class ExampleViewModel @ViewModelInject constructor(
  private val repository: ExampleRepository,
  @Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
  ...
}

//Activity / Fragment
@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {
  private val exampleViewModel: ExampleViewModel by viewModels()
  ...
}

但是如何将HiltViewModelFactory 一起使用?

似乎答案在@Assisted,但我不知道如何

如何告诉 hilt 我喜欢它向 ViewModel 注入存储库接口,同时仍然允许 ViewModelFactory 在运行时使用参数初始化 ViewModel?

【问题讨论】:

标签: android dagger-hilt


【解决方案1】:

感谢@Elye,下一篇文章很有帮助。推荐阅读。

Passing Activity Intent Data to ViewModel through Injection

Injecting ViewModel with Dagger Hilt

似乎大部分工厂都不需要,因为大多数viewmodel初始参数取自之前的片段,可以通过SavedStateHandle访问,如果标记为@Assisted,则会自动注入

为了设置刀柄,我使用了以下代码实验室教程

Using Hilt in your Android app

然后,viewModel 注入仅使用下一个代码自动完成

请注意,正如 fabioCollini here 所指出的,savedStateHandle 似乎也可以通过简单地将参数名称作为键来从 safe args 获取值。事实上,这就是我在以下示例中所做的。 ps:为了使安全参数更“安全”,我确实尝试用ItemsFragmentArgs 替换SavedStateHandle,希望它可以工作,但应用程序没有编译。我确实希望它会在未来实施(如果已经实施,请告诉我)

//ItemFragment file
@AndroidEntryPoint
class ItemsFragment : Fragment() {

    private val viewModel: ItemsViewModel by viewModels()

    //use viewModel as you would. No need to initialize.
}

//Module file - if you have any repository, remember to bind it 
//or provide the exact implementation as noted in code-labs
@InstallIn(ApplicationComponent::class)
@Module
abstract class DatabaseModuleBinder {

    @Binds
    abstract fun bindGlistRepository(impl: FirestoreGlistRepository): GlistRepository

}


//ItemsViewModel file - lastly, anotate as follows and take your arguments 
//from savedStateHandle (for safe args, use variable name as key)
class ItemsViewModel @ViewModelInject constructor(private val glistRepo: GlistRepository,
                     @Assisted private val savedStateHandle: SavedStateHandle) : ViewModel() {

    private val glistLiveDate = glistRepo.getGlistLiveData(
        savedStateHandle.get<String>("listId")!!
    )

..
}

希望对大家有帮助,如有错误,请告诉我

【讨论】:

  • 我的问题是,我在 Fragment 中使用 viewmodel,而在 Fragment 上我没有提到 @AndroidEntryPoint。我在 Activity 上提到了这一点,但在 Fragment 上没有提到。
  • 要在视图模型中使用安全参数,您可以实现我在这里展示的内容:stackoverflow.com/a/64814055/1842900
  • @ViewModelInject@Assisted 现已弃用。
【解决方案2】:

将您的 ScoreViewModelFactory 传递到 viewModel 的内置 ktx 扩展中。您还可以使用 SavedStateHandle 本身和 defaultViewModelProviderFactory 来使用 Activity/Fragment 参数。

/*
Gradle Dependencies
def lifecycle_version = "2.2.0"
def hiltLifeVersion = "1.0.0-alpha01"
def hiltVersion = "2.28.1-alpha"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "com.google.dagger:hilt-android:$hiltVersion"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01"
implementation "androidx.hilt:hilt-work:$hiltLifeVersion"
implementation "androidx.hilt:hilt-common:1.0.0-alpha01"
kapt "com.google.dagger:hilt-android-compiler:$hiltVersion"
kapt "androidx.hilt:hilt-compiler:$hiltLifeVersion"
*/

import androidx.fragment.app.viewModels

@AndroidEntryPoint
class ExampleFragment : Fragment(R.layout.example_fragment) {

    //internally using defaultViewModelProviderFactory 
    private val viewModel : ExampleViewModel by viewModels()

    //or you own viewmodal factory instance --> scoreViewModelFactory
    private val viewModel : ExampleViewModel by viewModels { scoreViewModelFactory }

}

class ExampleViewModel @ViewModelInject constructor(
    private val repository: ExampleRepository,
    @Assisted override val savedStateHandle: SavedStateHandle
) : ViewModel() {

    //bundle args -> String, Int, Parcelable etc.. 
    private val arg1LiveData: MutableLiveData<String> = 
                         savedStateHandle.getLiveData("arg1", "")

}

Fragment viewmodel 的内置 ktx-extension

@MainThread
inline fun <reified VM : ViewModel> Fragment.viewModels(
    noinline ownerProducer: () -> ViewModelStoreOwner = { this },
    noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer)

【讨论】:

  • scoreViewModelFactory 来自哪里?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-09
  • 2021-07-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多