创建 ViewModel 或 AndroidViewModel 的方法

Posted 小陈乱敲代码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了创建 ViewModel 或 AndroidViewModel 的方法相关的知识,希望对你有一定的参考价值。

有几种方法可以创建ViewModel和androidViewModel. 本文向您展示了创建它们的 Kotlin 示例。

这是您可能拥有的示例ViewModel或课程。AndroidViewModel

class MyViewModel: ViewModel() 

class MyAndroidViewModel (app: Application)
    : AndroidViewModel(app) 

这里的代码示例用于片段类。所以它可能在活动类中不起作用。如果您将它们复制并粘贴到您的活动类中,则需要进行少量修改。

如果你不熟悉 Kotlin,可以先通过这里的一些简单示例 来了解一些重要的概念,例如“委托”。

手动创建 - 不要这样做!

private val viewModel = MyViewModel()
private val androidViewModel = 
    MyAndroidViewModel(requireActivity().application)

这只有在你不旋转手机的情况下才有效。当你旋转手机时,一个活动或片段被破坏并重新创建。将再次创建ViewModel或AndroidViewModel的新实例。因此,屏幕旋转之前的所有数据都会丢失。这违背了ViewModel架构的目的。您希望ViewModel通过活动或片段破坏存活下来。

我犯了这个错误,因为我不理解使用ViewModelProvider创建ViewModel的原因。

带有 ViewModelProvider 的 lateinit var

private lateinit var viewModel: MyViewModel
private lateinit var androidViewModel: MyAndroidViewModel
override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View 

    viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
    androidViewModel =          
        ViewModelProvider(this).get(MyAndroidViewModel::class.java)

使用ViewModelProvider是正确的创建方式ViewModel。创建活动或片段时,ViewModelProvider它足够聪明,可以重用第一个创建的ViewModel实例。

如果ViewModel没有改变(这很可能是真的),val在这里使用 Kotlin 变量是一个更好的选择。

通过使用ViewModelProvider

要使用val变量,请使用by lazy属性初始化。首次访问变量时,将执行委托块。

private val viewModel: MyViewModel by lazy 
    ViewModelProvider(this).get(MyViewModel::class.java)


private val androidViewModel: MyAndroidViewModel by lazy 
    ViewModelProvider(this).get(MyAndroidViewModel::class.java)

代码看起来比lateinit var解决方案干净得多。然而,另一种优雅的方式是使用by viewModelsor by activityViewModels。

通过视图模型/活动视图模型

要使用此属性委托,需要将以下依赖项添加到 build.gradle(模块级)。该版本只是一个示例,您可以使用更高版本或最新版本。

implementation 'androidx.fragment:fragment-ktx:1.3.6'

下面的代码太棒了!它本质上与by lazy不需要指定ViewModelProvider. 它会自动为您计算出来。

private val viewModel: MyViewModel by viewModels()
private val androidViewModel: MyAndroidViewModel by viewModels()

如果您想ViewModel在同一活动中共享不同的片段。您可以使用by activityViewModels.

private val viewModel: MyViewModel by activityViewModels()
private val androidViewModel: MyAndroidViewModel 
    by activityViewModels()

通过 viewModels(自定义构造函数参数)

ViewModel将其他对象传递给构造函数是很常见的。以下示例将Repository对象传递到MyViewModeland MyAndroidViewModel。

class MyViewModel(private val repository: Repository)
    : ViewModel() 


class MyAndroidViewModel(app: Application, repository: Repository)
    : AndroidViewModel(app) 

有一个自定义的构造函数参数ViewModel有点复杂。您需要有一个自定义ViewModel工厂来创建您的ViewModel.

要创建自定义ViewModel工厂,您可以继承自ViewModelProvider.NewInstanceFactory.

class MyViewModelFactory(private val repository: Repository)
    : ViewModelProvider.NewInstanceFactory() 

    override fun <T : ViewModel> create(modelClass: Class<T>): T 

        if (modelClass.isAssignableFrom(MyViewModel::class.java)) 
            return MyViewModel(repository) as T
        

        throw IllegalArgumentException("Unknown ViewModel class")
    

对于自定义AndroidViewModel工厂,您可以继承自ViewModelProvider.AndroidViewModelFactory

class MyAndroidViewModelFactory(
    private val app: Application,
    private val repository: Repository)
    : ViewModelProvider.AndroidViewModelFactory(app) 

    override fun <T : ViewModel> create(modelClass: Class<T>): T 

        if (modelClass.isAssignableFrom(
                MyAndroidViewModel::class.java)) 

            return MyAndroidViewModel(app, repository) as T
        

        throw IllegalArgumentException("Unknown ViewModel class")
    

事实上,我们可以只实现ViewModelProvider.Factoryinterfacefor both MyViewModelFactoryand MyAndroidViewModelFactory。可以在此处找到示例。

要ViewModel使用自定义构造函数参数创建,请使用by viewModels委托属性。

private val viewModel: MyViewModel by viewModels 

    MyViewModelFactory(Repository())


private val androidViewModel: MyAndroidViewModel by viewModels 

    MyAndroidViewModelFactory(
        requireActivity().application,
        Repository())

如果您希望您在同一活动中的不同片段中生存,您可以替换by viewModels为。by ActivityViewModelsViewModel

private val viewModel: MyAndroidViewModel by activityViewModels 

    MyViewModelFactory(Repository())


private val androidViewModel: MyAndroidViewModel 
    by activityViewModels 

        MyAndroidViewModelFactory(
            requireActivity().application,
            Repository())
    

[更新 - 2021 年 11 月 7 日]:您也可以使用by lazyandViewModelProvider()代替,by viewModels它应该仍然可以工作。它不能用于替换by activityViewModels,因为创建的ViewModel不会在不同的片段之间共享。因此,这仅供您参考和了解。

private val viewModel: MyViewModel by lazy 
    val factory = MyViewModelFactory(Repository())

    ViewModelProvider(this, factory).get(MyAndroidViewModel::class.java)

private val viewModel: MyAndroidViewModel by lazy 
    val factory = MyAndroidViewModelFactory(
        requireActivity().application, 
        Repository())

    ViewModelProvider(this, factory).get(MyAndroidViewModel::class.java)

我的常见做法

编程的有趣之处在于有很多方法可以做同样的事情。了解差异,让你成为更好的程序员

我默认使用最后一种方法,因为我的 ViewModel 中通常有自定义构造函数参数。

此外,我通常使用by activityViewModels而不是by viewModels,这样可以跨不同片段共享数据。这节省了我研究如何将数据传递到不同片段的时间。例如,使用Bundleto在片段之间共享数据。

默认情况下,我也使用AndroidViewModel而不是ViewModel,因为我通常需要从应用程序上下文访问字符串资源和系统服务。网上正在讨论一些缺点,但我还没有完全理解这一部分。目前,AndroidViewModel对我来说很好。

这些是我的常见做法。我不确定其他安卓开发者是否同意我的观点。请告诉我你的想法。

以上是关于创建 ViewModel 或 AndroidViewModel 的方法的主要内容,如果未能解决你的问题,请参考以下文章

jetpack之ViewModel

jetpack之ViewModel

Jetpack组件---ViewModel 概览

ViewModel学习

Android Jetpack之ViewModel源码分析

如何通过 Viewmodel 调用 sqlite 方法?