如何使用带片段的视图模型+房间?

How to use viewmodel + room with fragments?

我正在查看 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<com.example.tabtester.ViewModels.MyViewModel> has no zero argument constructor。我找不到任何向我展示如何在 Kotlin 中提供构造函数的 SO 帖子或文档。

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

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

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

希望得到任何帮助或能更清楚地解释这一点的文档。

by viewModels()by activityViewModels() 和 (now deprecated) ViewModelProviders.of() all feed into one method: the ViewModelProvider constructor:

的底层 API
ViewModelProvider(viewModelStore: ViewModelStore, factory: ViewModelProvider.Factory)

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

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

因此,如果您需要自定义 Factory,您必须 始终 将该工厂传递到所有可以创建该 ViewModel 的地方(记住,由于处理死亡和重生,不能保证您的 HomeFragment 将是创建您的 ViewModel) 的第一个片段。

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

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