大家好
我目前正在开发一个缓存应用程序,遇到以下情况:DAO 函数返回一个 Flow,表示来自本地数据库的数据流。在存储库中,我从 DAO 函数收集数据,在 ViewModel 中,我从存储库函数收集数据。
我想讨论从存储库和 ViewModel 收集数据的最佳实践。在两个地方收集数据是否被认为是一个好的做法?这种方法有任何潜在的缺点或问题吗?
为了提供更多信息,本地数据库充当应用程序的“事实来源”,并且主要数据处理发生在那里。由于 DAO 函数返回 Flow 并且存储库函数返回 Flow,因此似乎有必要从存储库中的 DAO 函数收集数据以观察数据库的更新。
我非常感谢您关于从存储库和 ViewModel 收集数据的最佳实践的建议和经验,同时考虑使用协程和 Flow。
道:
@Dao
interface MovieDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun saveMovies(moviesEntity: MoviesEntity)
@Query("SELECT * FROM movies")
fun getMovies(): Flow<List<MoviesEntity>>?
@Query("DELETE FROM movies")
suspend fun clearMoviesListing()
}
接口MovieRepository:
interface MovieRepository {
fun getMoviesResult(
category: String,
fetchFromRemote: Boolean,
): Flow<Resource<List<Movies>>>
}
MovieRepositoryImpl:
@RequiresExtension(extension = Build.VERSION_CODES.S, version = 7)
class MovieRepositoryImpl(
private val movieApi: MovieApi,
private val movieDao: MovieDao
) : MovieRepository {
override fun getMoviesResult(
category: String,
fetchFromRemote: Boolean
): Flow<Resource<List<Movies>>> {
return flow {
while (true) {
emit(Resource.Loading(true))
movieDao.getMovies()?.collect { localListing ->
if (localListing.isNotEmpty() && !fetchFromRemote) {
emit(Resource.Loading(false))
emit(
Resource.Success(data = localListing.map {
it.toMovies()
})
)
} else {
val remoteListing = try {
movieApi.getMovies(category = category)
} catch (e: IOException) {
e.printStackTrace()
emit(Resource.Error("Couldn't Reach Server, Check Your Internet Connection, Try Refresh"))
null
} catch (e: HttpException) {
e.printStackTrace()
emit(Resource.Error("Couldn't Reach Server, Check Your Internet Connection."))
null
}
if (remoteListing !== null) {
emit(Resource.Loading(false))
remoteListing.let { movie ->
movieDao.clearMoviesListing()
movieDao.saveMovies(movie.toMoviesEntity())
movieDao.getMovies()!!.collect() { movieEntity ->
emit(
Resource.Success(data = movieEntity.map {
it.toMovies()
})
)
}
}
}
emit(Resource.Error(message = ""))
emit(Resource.Loading(false))
}
}
}
}
}
}
电影屏幕视图模型:
private fun getMovieResult(category: String, fetchFromRemote: Boolean) {
viewModelScope.launch {
movieRepository.getMoviesResult(category, fetchFromRemote)
.collect { results ->
when (results) {
is Resource.Success -> {
results.data?.let { result ->
state = state.copy(movies = result)
}
}
is Resource.Error ->
state = state.copy(
error = results.message ?: "An unexpected error occurred"
)
is Resource.Loading -> state = state.copy(isLoading = results.isLoading)
}
}
}
}
我不确定在 ViewModel 中再次收集数据是否合适,或者是否有更有效的方法。我想确保我遵循最佳实践并在我的应用程序中充分利用协程和 Flow。
lateinit var movieListLiveData: MutableLiveData<List<Movies>>
private fun getMovieResult(category: String, fetchFromRemote: Boolean) {
movieListLiveData = movieRepository.getMoviesResult(category, fetchFromRemote).asLiveData()
}
并使用实时数据来更新您的 UI
asLiveData 运算符将 Flow 转换为具有可配置超时的 LiveData。 就像 liveData 构建器一样,超时将帮助 Flow 在重新启动后幸存下来。如果在超时之前有另一个屏幕观察,则流程不会被取消。