如何使用 MVVM 模式从 Firestore 检索数据

How to retrieve data from Firestore with the MVVM pattern

我正在创建一个 android 遵循 MVVM 赞助人的应用程序,目的是从 Firebase 集合中检索数据。

在应用此模式之前,我进行了概念验证,并且能够从 Firebase 集合中检索数据。但是一旦我应用了 MVVM,我就无法从那个集合中获取数据,我的屏幕上什么也没有显示。我无法 return 将存储库中的数据绘制在屏幕上。

这是我的代码:

型号:

data class PotatoesData(
    val modifiedDate: String,
    var potatoes: List<Potato>
) 
data class Potato(
    val type: String,
    val site: String
)

州:

data class PotatoesState(
    val isLoading: Boolean = false,
    val potatoes: List<Potato> = emptyList(),
    val error: String = ""
)

模型视图:

@HiltViewModel
class PotatoesViewModel @Inject constructor(
    private val getPotatoesDataUseCase: GetPotatoesData
) : ViewModel() {

    private val _state = mutableStateOf(PotatoesState())
    val state: State<PotatoesState> = _state

    init {
        getPotatoes()
    }

    private fun getPotatoes() {

        getPotatoesDataUseCase().onEach { result ->
            when (result) {
                is Resource.Success -> {
                    _state.value = PotatoesState(potatoes = result.data?.potatoes ?: emptyList())
                }
                is Resource.Error   -> {
                    _state.value = PotatoesState(
                        error = result.message ?: "An unexpected error occurred"
                    )
                }
                is Resource.Loading -> {
                    _state.value = PotatoesState(isLoading = true)
                }
            }
        }.launchIn(viewModelScope)
    }
}

用例:

class GetPotatoesData @Inject constructor(
    private val repository: PotatoRepository
) {
    operator fun invoke(): Flow<Resource<PotatoesData>> = flow {
        try {
            emit(Resource.Loading())
            val potatoes = repository.getPotatoesData()
            emit(Resource.Success(potatoes))
        } catch (e: IOException) {
            emit(Resource.Error("Couldn't reach server. Check your internet connection."))
        }
    }
}

存储库实现:

class PotatoRepositoryImpl : PotatoRepository {

    override suspend fun getPotatoesData(): PotatoesData {

        var potatoes = PotatoesData("TEST", emptyList())

        FirestoreProvider.getLastPotatoes(
            { potatoesData ->
                if (potatoesData != null) {
                    potatoes = potatoesData 
                }
            },
            { 
                potatoes 
            }
        )

        return potatoes 
    }
}

Firestore 提供商:

object FirestoreProvider {

    private val incidentsRef = FirebaseFirestore.getInstance().collection(FirestoreCollection.POTATOES.key)

    fun getLastPotatoes(
        success: (potatoesData: PotatoesData?) -> Unit,
        failure: () -> Unit
    ) {

        val query: Query = orderBy(FirestoreField.CREATED_DATE, Query.Direction.DESCENDING).limit(1)
        val querySnapshot: Task<QuerySnapshot> = query.get()

        querySnapshot
            .addOnSuccessListener {
                if (!querySnapshot.result.isEmpty) {
                    val document = querySnapshot.result.documents[0]
                    val potatoesDataDB: PotatoesDataDto? = document.toObject(PotatoesDataDto::class.java)
                    potatoesDataDB?.let {
                        success(potatoesDataDB.toPotatoesData())
                    } ?: run {
                        success(null)
                    }
                } else {
                    success(null)
                }
            }
            .addOnFailureListener {
                failure()
            }
    }

    private fun orderBy(field: FirestoreField, direction: Query.Direction): Query {

        return incidentsRef.orderBy(field.key, direction)
    }

}

感谢您提供的任何帮助!提前致谢!

我认为错误在于您处理 Firestore 回调的方式。在 FirestoreProvider 中:回调将在函数 getLastPotatoes return 之后触发。尝试使该功能挂起并使用 suspendCoroutine 等待回调和 return 结果。它看起来像:

suspend fun getLastPotatoes() = suspendCoroutine <PotatoesData?> { continuation ->

    val query: Query = orderBy(FirestoreField.CREATED_DATE, Query.Direction.DESCENDING).limit(1)
    val querySnapshot: Task<QuerySnapshot> = query.get()

    querySnapshot
        .addOnSuccessListener {
            if (!querySnapshot.result.isEmpty) {
                val document = querySnapshot.result.documents[0]
                val potatoesDataDB: PotatoesDataDto? = document.toObject(PotatoesDataDto::class.java)
                potatoesDataDB?.let {
                    continuation.resume(potatoesDataDB.toPotatoesData())
                } ?: run {
                    continuation.resume(null)
                }
            } else {
                continuation.resume(null)
            }
        }
        .addOnFailureListener {
            continuation.resumeWithException(...)
        }
}

suspendCoroutine 暂停执行它的协程,直到我们决定通过调用适当的方法继续 - Continuation.resume....

在你的 PotatoRepositoryImpl:

override suspend fun getPotatoesData(): PotatoesData {

    var potatoes = PotatoesData("TEST", emptyList())

    try {
        val potatoesData = FirestoreProvider.getLastPotatoes()
        if (potatoesData != null) {
            potatoes = potatoesData
        }
    } catch (e: Exception) {
        // handle Exception
    }
   
    return potatoes 
}