Kotlin Flow "First Offline" 方法



我正在开发一个全新的android应用程序,我想使用Kotlin Flow实现离线优先策略,就像我们使用RxJava一样。当我使用Rx时,我使用下面的代码作为离线优先功能。

private fun getOfflineFirst(param: Param): Observable<T> =
Observable.concatArrayEagerDelayError(
getOffline(param), getRemote(param)

getOffline和getRemote函数将返回一个可观察的对象。我使用下面的代码来使用Flow实现相同的结果。

private suspend fun getOfflineFirst(param: Param) = flow {
getLocal(param)
.onCompletion {
getRemote(param).collect {
emit(it)
}
}.collect { emit(it) }
}

getLocal和getRemote将返回一个Flow对象。我还在我的一个操场项目中使用了另一个逻辑,如下所示:

suspend fun getResult(param: PARAM, strategy: QueryStrategy): Flow<ResultResponse> = flow {
if (strategy.isRemote()) {
emit(getRemoteResult(param))
} else {
emit(getLocalResult(param))
emit(getRemoteResult(param))
}
}

在";否则";部分,它将先发出本地结果,然后发出远程结果,这就是我在这个场景中第一次脱机处理的方式。

但我不确定我是否使用了最好的方法。

有人能给我一些更好的方法吗?

使用这个抽象类来处理数据获取和存储。

/**
* A repository which provides resource from local database as well as remote 
endpoint.
*
* [RESULT] represents the type for database.
* [REQUEST] represents the type for network.
*/
@ExperimentalCoroutinesApi
abstract class NetworkBoundRepository<RESULT, REQUEST> {
fun asFlow() = flow<State<RESULT>> {
// Emit Loading State
emit(State.loading())
try {
// Emit Database content first
emit(State.success(fetchFromLocal().first()))
// Fetch latest posts from remote
val apiResponse = fetchFromRemote()
// Parse body
val remotePosts = apiResponse.body()
// Check for response validation
if (apiResponse.isSuccessful && remotePosts != null) {
// Save posts into the persistence storage
saveRemoteData(remotePosts)
} else {
// Something went wrong! Emit Error state.
emit(State.error(apiResponse.message()))
}
} catch (e: Exception) {
// Exception occurred! Emit error
emit(State.error("Network error! Can't get latest data."))
e.printStackTrace()
}
// Retrieve posts from persistence storage and emit
emitAll(fetchFromLocal().map {
State.success<RESULT>(it)
})
}
/**
* Saves retrieved from remote into the persistence storage.
*/
@WorkerThread
protected abstract suspend fun saveRemoteData(response: REQUEST)
/**
* Retrieves all data from persistence storage.
*/
@MainThread
protected abstract fun fetchFromLocal(): Flow<RESULT>
/**
* Fetches [Response] from the remote end point.
*/
@MainThread
protected abstract suspend fun fetchFromRemote(): Response<REQUEST>
}

并且在您的repo类中传递您的api接口和数据库repo

class Repository(private val api: ApiInterface, private val db: DBRepository) {
/**
* Fetched the posts from network and stored it in database. At the end, data from 
persistence
* storage is fetched and emitted.
*/
fun getAllArticles(): Flow<State<List<Article>>> {
return object : NetworkBoundRepository<List<Article>, ArticlesResponse>() {
override suspend fun saveRemoteData(response: ArticlesResponse) =
db.getNewsDao().insertALLItems(response.article!!)
override fun fetchFromLocal(): Flow<List<Article>> = 
db.getNewsDao().getItems()
override suspend fun fetchFromRemote(): Response<ArticlesResponse> = 
api.getArticles()
}.asFlow().flowOn(Dispatchers.IO)
}
}

首先,这取决于您希望有多大的可变性。其次,还有Dropbox的Store解决方案,它是解决这些问题的更通用(更复杂(的解决方案。

我不太理解你的第一个函数getOfflineFirst,它可以像其他分支中的第二个例子一样完成。

我不建议采用这种方法。首先,在另一个flow中从另一flowcollect()不是一个好的选择。其次,您必须在RxFlow之间做出决定—您不需要两者。

为此,您可以始终使用map()方法。我会这样做:

getData() = getLocal(param)
.map{ whatGetLocalReturns ->
return@map getRemote(param)
}
.map{ whatGetRemoteReturns ->
return@map decideResult(whatGetRemoteReturns, strategy)
}

然后你可以做:

getData().collect{ result ->
// there you go :) 
}

我省略了一些细节,但我相信你能理解我的确切意思。当然,别忘了穿线和其他一切。

相关内容

最新更新