怎样正确编写 Android ViewModel 并将逻辑移到外部?

huangapple go评论62阅读模式
英文:

How to write Android ViewModel properly and move logic out of it?

问题

我正在尝试在Android/Kotlin中使用MVVM架构,结合ViewModelViewBinding和Retrofit2。

我不知道如何将应用程序逻辑从ViewModel中移动。我不能简单地移动具有逻辑的方法,因为它们在viewModelScope上运行,并将结果放入我的ViewModel中的可观察对象。

或者我可以吗?

例如,我有一些ArrayList(要在某个ListView上显示)。

// items ArrayList
private val _stocktakings = 
    MutableLiveData<ArrayList<InventoryStocktakingWithCountsDto?>>(ArrayList())
val stocktakings : LiveData<ArrayList<InventoryStocktakingWithCountsDto?>> get() =
_stocktakings

// selected item
private val _selected_stocktaking = MutableLiveData<Int>
val selected_stocktaking : LiveData<Int> get() = _selected_stocktaking 

并且有一个从我的fragment调用的函数:

public fun loadStocktakings() {
    viewModelScope.launch {
        Log.d(TAG, "Load stocktakings requested")
        clearError()
        try {
            with(ApiResponse(ApiAdapter.apiClient.findAllStocktakings())){
                if (isSuccessful && body != null){
                    Log.d(TAG, "Load Stocktakings done")
                    setStocktakings(body)
                } else {
                    val e = "Can't load stocktakings, API error: {${errorMessage}}"
                    Log.e(TAG, e)
                    HandleError("Can't load stocktakings, API error: {${e}}") // 将错误消息放入val lastError MutableLiveData...
                }
            }
        } catch (t : Throwable) {
            Log.e(TAG, "Can't load stocktakings, connectivity error: ${t.message}")
            HandleError("Can't load stocktakings, API error: {${e}}") // 将错误消息放入val lastError MutableLiveData...
        }
    }
}

现在我想添加另一个函数,用于更改stocktakings中某个字段。也许像这样:

public fun setSelectedStocktakingComplete() {
    stocktakings.value[selected_stocktaking.value].isComplete = true;

    // 调用某个API函数... 另外15行代码?
}

如何正确做到这一点?

我觉得我看错了教程... 这将导致ViewModel变得臃肿,充满了viewModelScope.launch、错误处理,我无法想象当我开始添加数据/表单验证时会发生什么...

英文:

I'm trying to use MVVM with ViewModel, ViewBinding, Retrofit2 in Android/Kotlin.

I don't know how to move application logic from ViewModel. I can't just move methods with logic because they run on viewModelScope and put results into observable objects in my ViewModel.

Or maybe I can?

For example I have some ArrayList (to show on some ListView).

// items ArrayList
private val _stocktakings = 
    MutableLiveData&lt;ArrayList&lt;InventoryStocktakingWithCountsDto?&gt;&gt;(ArrayList())
val stocktakings : LiveData&lt;ArrayList&lt;InventoryStocktakingWithCountsDto?&gt;&gt; get() =
_stocktakings

// selected item
private val _selected_stocktaking = MutableLiveData&lt;Int&gt;&gt;
val selected_stocktaking : LiveData&lt;Int&gt; get() = _selected_stocktaking 

And a function that is called from my fragment:

public fun loadStocktakings() {
    viewModelScope.launch {
        Log.d(TAG, &quot;Load stocktakings requested&quot;)
        clearError()
        try {
            with(ApiResponse(ApiAdapter.apiClient.findAllStocktakings())){
                if (isSuccessful &amp;&amp; body != null){
                    Log.d(TAG, &quot;Load Stocktakings done&quot;)
                    setStocktakings(body)
                } else {
                    val e = &quot;Can&#39;t load stocktakings, API error: {${errorMessage}}&quot;
                    Log.e(TAG, e)
                    HandleError(&quot;Can&#39;t load stocktakings, API error: {${e}}&quot;) // puts error message into val lastError MutableLiveData...
                }
            }
        } catch (t : Throwable) {
            Log.e(TAG, &quot;Can&#39;t load stocktakings, connectivity error: ${t.message}&quot;)
            HandleError(&quot;Can&#39;t load stocktakings, API error: {${e}}&quot;) // puts error message into val lastError MutableLiveData...
        }
    }
}

Now I want to add another function that changes some field in one of stocktakings. Maybe something like:

public fun setSelectedStocktakingComplete() {
    stocktakings.value[selected_stocktaking.value].isComplete = true;

    // call some API function... another 15 lines of code?
}

How to do it properly?

I feel I have read wrong tutorials... This will end with fat viewmodel cluttered with viewModelScope.launch, error handling and I can't imagine what will happen when I start adding data/form validation...

答案1

得分: 1

这里有一些关于这个的提示:

  1. 确保ViewModel只负责保存和管理与UI相关的数据。
  2. 避免在ViewModel中放置业务逻辑。相反,将其封装在单独的类中,例如Repository或Interactor类。
  3. 使用LiveData来观察ViewModel中的数据更改,并相应地更新UI。
  4. 避免在ViewModel中进行网络或数据库调用。而是使用Repository模式来管理数据操作,并通过LiveData或其他可观察对象将数据提供给ViewModel。
  5. 确保ViewModel不持有上下文引用,例如Activity或Fragment。
  6. 如果需要,可以使用ViewModel工厂来提供依赖项给ViewModel。
  7. 这样可以确保您的ViewModel简单、易于测试和可扩展。它还使代码库更容易维护,因为业务逻辑与UI逻辑分开。
英文:

Here, some tip for that

  1. Make sure the ViewModel is only responsible for holding and managing
    UI-related data.
  2. Avoid putting business logic in the ViewModel. Instead, encapsulate
    it in separate classes, such as Repository or Interactor classes.
  3. Use LiveData to observe data changes in the ViewModel and update the
    UI accordingly.
  4. Avoid making network or database calls in the ViewModel. Instead,
    use the Repository pattern to manage data operations and provide the
    data to the ViewModel through a LiveData or other observable object.
  5. Make sure the ViewModel does not hold context references, such as
    Activity or Fragment.
  6. Use a ViewModel factory to provide dependencies to the ViewModel, if
    necessary.
  7. you can ensure that your ViewModel is simple, easy to test,
    and scalable. It also makes it easier to maintain your codebase, as
    the business logic is separated from the UI logic.

hope you understand

huangapple
  • 本文由 发表于 2023年2月6日 09:36:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75356672.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定