我正在开发一个全新的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
中从另一flow
到collect()
不是一个好的选择。其次,您必须在Rx
和Flow
之间做出决定—您不需要两者。
为此,您可以始终使用map()
方法。我会这样做:
getData() = getLocal(param)
.map{ whatGetLocalReturns ->
return@map getRemote(param)
}
.map{ whatGetRemoteReturns ->
return@map decideResult(whatGetRemoteReturns, strategy)
}
然后你可以做:
getData().collect{ result ->
// there you go :)
}
我省略了一些细节,但我相信你能理解我的确切意思。当然,别忘了穿线和其他一切。