我正在编写一个Android应用,并尝试实现存储库模式,但有一些特殊要求
1)我想拥有1到N个订阅者,所有订阅者都可以看到仓库的更改。数据是否更改
2)我想在内存缓存和磁盘缓存中本地缓存数据,并且还具有用于数据的网络源
3)我希望能够根据传入的标志的值强制从网络刷新
4)不应将重复的数据返回给订户
5)网络错误不应终止流。
下面显示了我当前的实现,并且似乎运行良好,但是我不确定的部分是,通过仅调用subscribe()来订阅我可以在存储库中观察到的网络是否是个坏主意并更新数据库,然后将事件传播到所有订户。在保持多播的同时,有没有更好的方法来包含可选的强制网络更新?
我以前曾尝试这样做:
fetchDataFromMemory()
.concatWith(fetchDataFromDb())
.flatMap {
if (forceRefresh) return@flatmap fetchDataFromNetwork
return@flatmap Observable.just(it)
}
但是,我遇到了一个无限循环,如果forceRefresh为true,则网络将更新db,这将发出一个可观察到的信号,该信号将调用flatMap,后者将进行网络刷新,从而将db等更新为无限。 >
class Repository {
override fun getAuthToken(): Observable<AuthEntity> {
// TODO make sure you understand how this works in all cases
return memory.getAuthEntity()
.concatWith(authDao.get())
.firstOrError()
.toObservable()
}
private fun refreshAccessTokenNonResponse(): Observable<AuthEntity> {
return getAuthToken()
.flatMap {
// Log.i("TAG", "kicking off token refresh")
networkSource
.refreshTokenNonResponse(BuildConfig.AUTH_URL + NetworkPaths.AUTH, it.refreshToken)
.doOnNext { authEntity ->
memory.setAuthEntity(authEntity)
authDao.upsertAuth(authEntity)
}
}
}
val requestsCache = ConcurrentHashMap<String, Observable<*>>()
val myObs: Observable<Pair<List<Data>?, Exception?>> = fetchDataFromMemory().concatWith(fetchDataFromDb()).share()
override fun getData(forceRefresh: Boolean): Observable<Pair<List<DataEntity>?, Exception?>> {
if (forceRefresh && !requestsCache.containsKey(GET_DATA)) {
fetchDataFromNetwork()
.subscribeOn(Schedulers.io())
.subscribe()
}
return myObs
.distinct()
}
fun fetchDataFromMemory(): Observable<Pair<List<Data>?, Exception?>> {
return memory.getData()
.map {
Log.i("TAG", "memory observable map")
Pair(it, null)
}
}
fun fetchDataFromDb(): Observable<Pair<List<Data>?, Exception?>> {
return dataDao
.getAllData()
.doOnNext {
Log.i("TAG", "RETURNING DATA FROM DB")
memory.setData(it)
}
.map { Pair(it, null) as Pair<List<Data>?, Exception?> }
}
fun fetchDataFromNetwork(): Observable<Pair<List<Data>?, Exception?>> {
return getAuthToken()
.flatMap {
Log.i("TAG", "Kicking off network request")
val netObs: Observable<Pair<Response?, Exception?>> = networkSource
.getDataNonResponse(BuildConfig.BASE_URL + NetworkPaths.DATA, "Bearer " + it.accessToken, body)
.map { response -> Pair(response, null) as Pair<DataResponse?, Exception?> }
requestsCache.put(GET_DATA, netObs)
return@flatMap netObs
}
.onErrorResumeNext { throwable: Throwable ->
Log.i("TAG", "Network Request Error: " + throwable.toString())
if (throwable is HttpException && throwable.code() == 401) {
return@onErrorResumeNext refreshAccessTokenNonResponse()
.flatMap {
networkSource
.getDataNonResponse(BuildConfig.BASE_URL + NetworkPaths.WORK_ORDER, "Bearer " + it.accessToken, body)
}
.map { response -> Pair(response, null) as Pair<DataResponse?, Exception?> }
.onErrorResumeNext { throwable: Throwable -> Observable.just(Pair(null, Exception(throwable))) }
}
return@onErrorResumeNext Observable.just(Pair(null, Exception(throwable)) as Pair<DataResponse?, Exception?>)
}
.map {
Log.i("TAG", "Network Request map")
Pair(it.first?.team?.data, it.second) as Pair<List<Data>?, Exception?>
}
.doOnNext { result: Pair<List<Data>?, Exception?> ->
Log.i("TAG", "Network Request do on next: " + result.toString())
requestsCache.remove(GET_DATA)
if (result.first != null) {
dataDao.insertAll(result.first as List<Data>)
memory.setData(result.first as List<Data>)
}
}
}
}