如何使用 Room 和 Retrofit 简化 MVVM Repository 功能

问题描述 投票:0回答:1

下面是Android MVVM设计模式的ViewModel类和Repository类。
(1)ViewModel在Coroutine中调用Repository的一个函数。
(2) 然后Repository检查SQLite中的数据。
(3) 如果SQLite中没有数据,则使用Retrofit请求数据。
(4) 从Retrofit中检索数据后,将数据保存在SQLite中并发送到ViewModel。

它工作正常,只是看起来协程的层次结构很复杂。(在 StateRepository.reqStates() 中)
当我在存储库中删除协程时,SQLite 不起作用。
如果这段代码可以更简单,请告诉我。
谢谢你。


class WeatherViewModel @Inject constructor(private val stateRepository: StateRepository) : ViewModel() {
    val ldWeather = MutableLiveData<Weather>()

    // Request Card data list to Repository
    fun reqStates() {
        viewModelScope.launch {
            val res = stateRepository.reqStates()

            withContext(Dispatchers.Main) {
                res?.let { ldStates.postValue(it) }
            }
        }
    }
}


open class StateRepository @Inject constructor(var api: StateApi, private val db: StateDatabase) {

    suspend fun reqStates(): List<State>? {
        return suspendCoroutine { continuation ->
            CoroutineScope(Dispatchers.IO).launch {
                // Search State list in SQLite
                val states = db.stateDao().getAll()
                if(!states.isNullOrEmpty()) {
                    continuation.resume(states)
                } else {
                    resSuspendCoroutine(continuation)
                }
            }
        }
    }

    private fun resSuspendCoroutine(continuation: Continuation<List<State>?>) {
        val call: Call<List<State>> = api.states()
        // Request State list to server when SQLite is empty
        call.enqueue(object : Callback<List<State>> {
            override fun onResponse(call: Call<List<State>>, response: Response<List<State>>) {
                continuation.resume(response.body())
                // Save state list in SQLite
                response.body()?.let { saveStatesInDB(it) }
            }

            override fun onFailure(call: Call<List<State>>, t: Throwable) {
                continuation.resume(null)
            }
        })
    }

    // Save state list in SQLite
    private fun saveStatesInDB(states: List<State>) {
        CoroutineScope(Dispatchers.IO).launch {
            states.forEach { db.stateDao().insert(it) }
        }
    }
}
android mvvm coroutine
1个回答
0
投票

创建

CoroutineScope
通常被认为是一种不好的做法 - 相反,您想要接受结构化并发的想法 - 每个协程都以具有明确的父/子关系的方式构建。

同样,您希望 UI 对更改做出反应 - 数据库中的更改应直接反映在 UI 中。这意味着您的 Room 数据库应该返回 可观察查询 -

Flow<List<Data>>
LiveData<List<Data>>
而不是仅仅返回一个
List<Data>
,它不会随着数据库的变化而变化。

这意味着您应该对

StateDao
StateApi
进行一些更改:

  1. 您的
    StateDao
    应返回
    Flow<List<State>>
    ,以便在数据库更改时自动发送更新:
@Dao
interface StateDao{
    @Query("SELECT * FROM states")
    fun getStates(): Flow<List<State>>
  1. 您的
    StateApi
    应该使用 Retrofit 对
    suspend
    方法的支持
    ,而不是使用
    Call
@GET("states")
suspend fun states(): List<State>

这允许我们将您的代码重写为:

class WeatherViewModel @Inject constructor(
    private val stateRepository: StateRepository
) : ViewModel() {

    // Get your states as a Flow, converting to a LiveData is optional
    val states: Flow<List<State>> = stateRepository.getStates().asLiveData()
}

open class StateRepository @Inject constructor(
    var api: StateApi,
    private val db: StateDatabase
) {

    // Return a Flow that automatically sends new data
    // as the database changes
    fun getStates(): Flow<List<State>> =
        db.stateDao().getStates().onEach { states ->
            // When the database emits an empty set of data, we'll
            // load from the network
            if (states.isEmpty()) {
                val newStates = api.states()
                // By using NonCancellable, we'll ensure the entire set of
                // data is added even if the user leaves the screen the
                // ViewModel is tied to while this is still going
                withContext(NonCancellable) {
                    newStates.forEach { db.stateDao().insert(it) }
                }
            }
        }
}

通常,您希望使用 WorkManager 安排网络调用,以便即使网络关闭,它们也会自动重试,但这只涉及在 isEmpty()

 块中安排工作并将 
StateApi
 调用移动到Worker 可以作为单独的步骤完成。

© www.soinside.com 2019 - 2024. All rights reserved.