创建 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对我来说很好。

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值