【问题标题】:How can I create a function that I can access from both a ViewModel and Activity?如何创建一个可以从 ViewModel 和 Activity 访问的函数?
【发布时间】:2021-04-30 22:42:26
【问题描述】:

我正在构建一个具有 MainActivity 和几个片段的 android 应用程序。我为每个片段使用 ViewModel 和 ViewModelFactory。我需要创建一个将执行网络请求的函数,并希望在其中一个片段中使用它,这样我就可以向用户显示一个进度条。当用户单击右上角菜单中的按钮时,我还想从覆盖函数 onOptionsItemSelected 中的 MainActivity 访问此函数。从这个按钮调用时,一切都应该在后台,在任务完成之前不需要向用户显示任何内容。

我尝试了两种方法。

  1. 将函数放在 ViewModel 中。这非常适合从片段调用并且一切正常。但是从 Main 活动调用时,我似乎无法正确引用 ViewModel。

我在 onCreate 之前将它放在 MainActivity 中

private lateinit var syncViewModel: SyncViewModel

这在 onCreate 中

syncViewModel = ViewModelProvider(this).get(SyncViewModel::class.java)

我收到以下错误消息。

java.lang.RuntimeException:无法启动活动 ComponentInfo java.lang.RuntimeException:无法创建 com 类的实例。.._.SyncViewModel

如何从 Activity 访问已经创建的 ViewModel 而不是创建一个?

  1. 所以我的第二次尝试是将此函数放在一个单独的文件中。然后我可以从 ViewModel 和 MainActivity 访问它。但我找不到更新 ViewModel 中进度条的方法。

在 ViewModel 中我有以下内容,然后这个片段中的 xml 引用了这个 syncProgress 值。

private var _syncProgress = MutableLiveData<Int>()

val syncProgress: LiveData<Int> = _syncProgress

连接这个单独的函数以更新我的 ViewModel 中的值,我缺少什么?

似乎选项#1 是正确的方法,但也许有第三种我不知道的方法会更好! 任何帮助表示赞赏!

更新 1

我的 ViewModel 定义为

class SyncViewModel(
val database: RoomDatabase,
application: Application) : AndroidViewModel(application) {

在片段中作为

// Create an Instance of the ViewModel Factory
val dataSource = RoomDatabase.getDatabase(application)
val viewModelFactory = SyncViewModelFactory(dataSource, application)

private lateinit var syncViewModel: SyncViewModel
syncViewModel = ViewModelProvider(this,viewModelFactory).get(SyncViewModel::class.java)

但我不确定我需要如何修改 MainActivity 中的 SyncViewModel?

【问题讨论】:

    标签: android kotlin android-fragments mvvm viewmodel


    【解决方案1】:

    我会遵循您的选项 1,并使用视图模型提供程序委托来简化获取您的参考。您应该在 Fragment 中指定一个 Activity 范围的 ViewModel,以便它获得与 Activity 相同的实例。

    在您的活动中:

    private val syncViewModel: SyncViewModel by viewModels()
    

    在你的片段中:

    private val syncViewModel: SyncViewModel by activityViewModels()
    

    由于可以使用 Application 实例获取数据库,因此可以通过将其移出构造函数来避免创建工厂,如下所示:

    class SyncViewModel(application: Application) : AndroidViewModel(application) {
    
        val database = RoomDatabase.getDatabase(application)
    

    如果您确实想继续使用您的工厂,可以将其作为参数传递给委托函数,例如:

    private val dataSource = RoomDatabase.getDatabase(application)
    private val syncViewModel: SyncViewModel by activityViewModels(SyncViewModelFactory(dataSource, application))
    

    【讨论】:

    • 我已经更新了我原来的问题以包含更多来自 Fragment 和 ViewModel,我不确定如何连接,因为我正在使用 ViewModelFactory?
    • 通过应用程序实例获取数据库现在更有意义,因为我看到了这种方式,但非常感谢您展示了这两种方式。很有帮助!我现在可以工作了!
    【解决方案2】:

    如果我理解正确,该函数应该在 ViewModel 之外,因为它也必须在其他地方访问。

    ViewModel 中的 LiveData 不会根据函数的状态进行更新,但解决此问题的一种方法是让 ViewModel 立即“监听”函数的当前状态。

    假设您通常会在存储库中将值/进度从 0 更新到 100,使用该进度更新 LiveData 对象并让 ViewModel 或/和 Activity 监听它。存储库中的 LiveData 并不完美,但根据我阅读的内容,它似乎是一个解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多