【问题标题】:How to use viewmodel + room with fragments?如何使用带有片段的视图模型+房间?
【发布时间】:2021-07-20 03:33:31
【问题描述】:

我正在查看 Android room with a view 的教程,并尝试将他们的模型扩展为使用 ViewModels 到多个片段,但不确定如何。

我的应用程序

class myApplication : Application() {


    companion object {
        var database: myDatabase? = null
        var repository: myRepository? = null
    }

    override fun onCreate() {
        super.onCreate()
        database = MyDatabase.getInstance(this)
        repository = MyRepository(database!!.myDatabaseDao)

    }
}

MyViewModel

class MyViewModel(private val repository: MyRepository) : ViewModel() {


    val allWords: LiveData<List<Words>> = repository.allWords.asLiveData()

    fun insert(word: Word) = viewModelScope.launch {
        repository.insert(word)
    }
}

class MyViewModelFactory(private val repository: MyRepository) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(MyViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return MyViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

HomeFragment

class HomeFragment : Fragment() {

    private val myViewModel: MyViewModel by activityViewModels()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        // Inflate the layout for this fragment
        var rootView = inflater.inflate(R.layout.fragment_home, container, false)

        return rootView
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        myViewModel.allWords.observe(viewLifecycleOwner) { words ->
            // Update the cached copy of the words in the adapter.
            words.let { Log.d("fragment", it.toString()) }
        }

    }
}

我还有几个其他片段,希望它们与HomeFragment 共享相同的ViewModel。我尝试了许多不同的方法,例如使用

myViewModel = ViewModelProviders.of(activity!!).get(MyViewModel::class.java)

但他们都给我Caused by: java.lang.InstantiationException: java.lang.Class&lt;com.example.tabtester.ViewModels.MyViewModel&gt; has no zero argument constructor。我找不到任何 SO 帖子或文档来告诉我如何在 Kotlin 中提供构造函数。

从概念上讲,我找不到任何关于究竟发生了什么以及视图模型是如何构建的(以及由什么构建的)的描述。在 Room with a View 教程中,给出的示例在 MainActivity

private val wordViewModel: WordViewModel by viewModels {
    WordViewModelFactory((application as WordsApplication).repository)
}

这对我来说是有道理的;您正在使用 Factory 实例化要在 MainActivity 中使用的 ViewModel。但是对于如何在 Fragments 中使用 ViewModels 的任何描述,我看不到 ViewModel 是在哪里构造的。如果您有多个正在构建 ViewModel 的片段?如果我使用 Fragments 那么这是否意味着我还需要一个 Activity 来构造 ViewModel,然后以某种方式在 Fragments 之间共享?

如果有任何帮助或文档可以更清楚地解释这一点,我们将不胜感激。

【问题讨论】:

  • by activityViewModels() 将工厂作为可选参数。为什么不将自定义工厂传递给它?
  • 我试了一下,抛出了java.lang.IllegalArgumentException: Cannot add the same observer with different lifecycles
  • 听起来你可以让你的共享 ViewModel 正常工作(它现在正在成功创建你的 ViewModel)。
  • 它编译并创建了一个 ViewModel,但不清楚它是否工作 - 我不确定如何实际观察我的数据并测试它是否是跨片段的共享视图模型实例。
  • 我修复了观察者错误,如果我按照您的建议添加private val myViewModel: MyViewModel by activityViewModels() {MyViewModelFactory(MyApplication.repository!!)},它看起来可能有效!但是,这是否意味着如果我将这一行添加到每个 Fragment 类中,它将使用 ViewModel 的共享实例而不是创建新实例?

标签: android kotlin android-fragments mvvm


【解决方案1】:

by viewModels()by activityViewModels() 和 (now deprecated) ViewModelProviders.of() 的底层 API 都输入一个方法:ViewModelProvider constructor

ViewModelProvider(viewModelStore: ViewModelStore, factory: ViewModelProvider.Factory)

这个构造函数有两个参数:

  • ViewModelStore 控制您创建的ViewModel 的存储和范围。例如,当您在 Fragment 中使用by viewModels() 时,它是Fragment 用作ViewModelStore。同样,by activityViewModels() 使用Activity 作为ViewModelStore
  • ViewModelProvider.Factory 控制 ViewModel 的构造,如果尚未为特定的 ViewModelStore 创建一个。

因此,如果您需要自定义 Factory,则必须始终将该 Factory 传递到所有可能创建该 ViewModel 的地方(请记住,由于进程死亡和重新创建,无法保证您的HomeFragment 将是创建您的ViewModel 的第一个片段。

private val myViewModel: MyViewModel by activityViewModels() {
    MyViewModelFactory(MyApplication.repository!!)
}

只要您使用activityViewModels(),您的ViewModel 的存储将始终处于活动级别,无论您使用什么工厂。

【讨论】:

    猜你喜欢
    • 2021-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 2013-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多