- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
Google的android架构组件教程here有一部分解释了如何抽象化通过网络获取数据的逻辑。在其中,他们创建了一个名为 NetworkBoundResource 的抽象类,使用 LiveData 创建一个 react 流作为所有反应性网络请求的基础。
public abstract class NetworkBoundResource<ResultType, RequestType> {
private final AppExecutors appExecutors;
private final MediatorLiveData<Resource<ResultType>> result = new MediatorLiveData<>();
@MainThread
NetworkBoundResource(AppExecutors appExecutors) {
this.appExecutors = appExecutors;
result.setValue(Resource.loading(null));
LiveData<ResultType> dbSource = loadFromDb();
result.addSource(dbSource, data -> {
result.removeSource(dbSource);
if (shouldFetch()) {
fetchFromNetwork(dbSource);
} else {
result.addSource(dbSource, newData -> result.setValue(Resource.success(newData)));
}
});
}
private void fetchFromNetwork(final LiveData<ResultType> dbSource) {
LiveData<ApiResponse<RequestType>> apiResponse = createCall();
// we re-attach dbSource as a new source, it will dispatch its latest value quickly
result.addSource(dbSource, newData -> result.setValue(Resource.loading(newData)));
result.addSource(apiResponse, response -> {
result.removeSource(apiResponse);
result.removeSource(dbSource);
//noinspection ConstantConditions
if (response.isSuccessful()) {
appExecutors.diskIO().execute(() -> {
saveCallResult(processResponse(response));
appExecutors.mainThread().execute(() ->
// we specially request a new live data,
// otherwise we will get immediately last cached value,
// which may not be updated with latest results received from network.
result.addSource(loadFromDb(),
newData -> result.setValue(Resource.success(newData)))
);
});
} else {
onFetchFailed();
result.addSource(dbSource,
newData -> result.setValue(Resource.error(response.errorMessage, newData)));
}
});
}
protected void onFetchFailed() {
}
public LiveData<Resource<ResultType>> asLiveData() {
return result;
}
@WorkerThread
protected RequestType processResponse(ApiResponse<RequestType> response) {
return response.body;
}
@WorkerThread
protected abstract void saveCallResult(@NonNull RequestType item);
@MainThread
protected abstract boolean shouldFetch();
@NonNull
@MainThread
protected abstract LiveData<ResultType> loadFromDb();
@NonNull
@MainThread
protected abstract LiveData<ApiResponse<RequestType>> createCall();
}
据我了解,这个类的逻辑是:
a) 创建一个名为“result”的MediatorLiveData作为主要返回对象,并设置其初始值为Resource.loading(null)
b) 从 Android Room db 获取数据作为 dbSource LiveData 并将其作为源 LiveData 添加到“结果”
c) 在 dbSource LiveData 的第一次发射中,从“结果”中删除 dbSource LiveData 并调用“shouldFetchFromNetwork()”,这将
鉴于此逻辑是正确的解释,我尝试重构此类以使用 RxJava Observables 而不是 LiveData。这是我成功重构的尝试(我删除了初始的 Resource.loading(null),因为我认为这是多余的)。
public abstract class NetworkBoundResource<ResultType, RequestType> {
private Observable<Resource<ResultType>> result;
@MainThread
NetworkBoundResource() {
Observable<Resource<ResultType>> source;
if (shouldFetch()) {
source = createCall()
.subscribeOn(Schedulers.io())
.doOnNext(apiResponse -> saveCallResult(processResponse(apiResponse)))
.flatMap(apiResponse -> loadFromDb().toObservable().map(Resource::success))
.doOnError(t -> onFetchFailed())
.onErrorResumeNext(t -> {
return loadFromDb()
.toObservable()
.map(data -> Resource.error(t.getMessage(), data))
})
.observeOn(AndroidSchedulers.mainThread());
} else {
source = loadFromDb()
.toObservable()
.map(Resource::success);
}
result = Observable.concat(
loadFromDb()
.toObservable()
.map(Resource::loading)
.take(1),
source
);
}
public Observable<Resource<ResultType>> asObservable() {return result;}
protected void onFetchFailed() {}
@WorkerThread
protected RequestType processResponse(ApiResponse<RequestType> response) {return response.body;}
@WorkerThread
protected abstract void saveCallResult(@NonNull RequestType item);
@MainThread
protected abstract boolean shouldFetch();
@NonNull
@MainThread
protected abstract Flowable<ResultType> loadFromDb();
@NonNull
@MainThread
protected abstract Observable<ApiResponse<RequestType>> createCall();
}
由于我是 RxJava 的新手,我的问题是我是否正确重构为 RxJava 并保持与此类的 LiveData 版本相同的逻辑?
最佳答案
public abstract class ApiRepositorySource<RawResponse extends BaseResponse, ResultType> {
// result is a Flowable because Room Database only returns Flowables
// Retrofit response will also be folded into the stream as a Flowable
private Flowable<ApiResource<ResultType>> result;
private AppDatabase appDatabase;
@MainThread
ApiRepositorySource(AppDatabase appDatabase) {
this.appDatabase = appDatabase;
Flowable<ApiResource<ResultType>> source;
if (shouldFetch()) {
source = createCall()
.doOnNext(this::saveCallResult)
.flatMap(apiResponse -> loadFromDb().toObservable().map(ApiResource::success))
.doOnError(this::onFetchFailed)
.onErrorResumeNext(t -> {
return loadFromDb()
.toObservable()
.map(data -> {
ApiResource apiResource;
if (t instanceof HttpException && ((HttpException) t).code() >= 400 && ((HttpException) t).code() < 500) {
apiResource = ApiResource.invalid(t.getMessage(), data);
} else {
apiResource = ApiResource.error(t.getMessage(), data);
}
return apiResource;
});
})
.toFlowable(BackpressureStrategy.LATEST);
} else {
source = loadFromDb()
.subscribeOn(Schedulers.io())
.map(ApiResource::success);
}
result = Flowable.concat(initLoadDb()
.map(ApiResource::loading)
.take(1),
source)
.subscribeOn(Schedulers.io());
}
public Observable<ApiResource<ResultType>> asObservable() {
return result.toObservable();
}
@SuppressWarnings("WeakerAccess")
protected void onFetchFailed(Throwable t) {
Timber.e(t);
}
@WorkerThread
protected void saveCallResult(@NonNull RawResult resultType) {
resultType.saveResponseToDb(appDatabase);
}
@MainThread
protected abstract boolean shouldFetch();
@NonNull
@MainThread
protected abstract Flowable<ResultType> loadFromDb();
@NonNull
@MainThread
protected abstract Observable<RawResult> createCall();
@NonNull
@MainThread
protected Flowable<ResultType> initLoadDb() {
return loadFromDb();
}
}
所以这是我在多次迭代后决定使用的。这目前正在生产中,并且适用于我的应用程序。以下是一些外带笔记:
创建一个BaseResponse
接口(interface)
public interface BaseResponse {
void saveResponseToDb(AppDatabase appDatabase);
}
并在您所有的 api 响应对象类中实现它。这样做意味着您不必在每个 ApiResource 中实现 save_to_database 逻辑,如果需要,您可以将其默认为响应的实现。
为简单起见,我选择在 onErrorResumeNext block 中处理 Retrofit 错误响应,但我建议您创建一个可以容纳所有这些逻辑的 Transformer 类。在这种情况下,我为 ApiResources 添加了一个额外的 Status
枚举值,称为 INVALID
用于 400 级响应。
您可能想使用 Reactive Streams 架构组件库来处理 LiveData
implementation "android.arch.lifecycle:reactivestreams:$lifecycle_version"
并在这个类中添加一个方法
public LiveData<ApiResource<ResultType>> asLiveData {
return LiveDataReactiveStreams.fromPublisher(result);
}
理论上,这会完美地工作,因为我们的 ViewModel 不必将 Observable 发射转换为 LiveData 发射或为 View 中的 Observable 实现生命周期逻辑。不幸的是,这个流在每次配置更改时都会重建,因为它会在任何调用的 onDestroy 中处理 LiveData(无论 isFinishing 是真还是假)。因此,我们必须管理这个流的生命周期,这首先违背了使用它的目的,或者每次设备旋转时都会重复调用。
这是一个 UserRepository
创建 ApiNetworkResource 实例的例子
@Singleton
public class UserRepository {
private final RetrofitApi retrofitApi;
private final AppDatabase appDatabase;
@Inject
UserRepository(RetrofitApi retrofitApi, AppDatabase appDatabase) {
this.retrofitApi = retrofitApi;
this.appDatabase = appDatabase;
}
public Observable<ApiResource<User>> getUser(long userId) {
return new ApiRepositorySource<UserResponse, User>(appDatabase) {
@Override
protected boolean shouldFetch() {
return true;
}
@NonNull
@Override
protected Flowable<User> loadFromDb() {
return appDatabase.userDao().getUserFlowable(userId);
}
@NonNull
@Override
protected Observable<UserResponse> createCall() {
return retrofitApi.getUserById(userId);
}
}.asObservable();
}
}
关于java - 重构谷歌的 NetworkBoundResource 类以使用 RxJava 而不是 LiveData,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49845532/
我正在使用 Retrofit、LiveData、Room (Android AAC)。 NetworkBoundResource是googlesimple提供的网络资源和房间结合的好 helper .
在 Google NetworkBoundResource 类中,我无法理解 MediatorLiveData 的 addSource() 和 removeSource() 的顺序被使用。 例如--在
您对如何使用 NetworkBoundResource 实现存储库模式有任何想法吗?和 Kotlin 协程?我知道我们可以使用 GlobalScope 启动协程,但这可能会导致协程泄漏。我想将 vie
我遇到了一些有线问题。当我第一次订阅时,它会进行网络调用并将数据保存到数据库,但是 loadFromDb() 永远不会执行,并且不会引发任何错误。 为什么会发生这种情况? Flowable>>
我的应用使用 Android 的 Architecture components库并显示从具有无限滚动效果的分页 REST api 获取的项目列表。 我想做的是使用 Paging Library结合
当我尝试为 Room Db 和 Retrofit 实现 NetworkBoundResource 和 Resource 帮助程序类时,它工作得很好。但是,我需要使用 Retrofit only wit
Google的android架构组件教程here有一部分解释了如何抽象化通过网络获取数据的逻辑。在其中,他们创建了一个名为 NetworkBoundResource 的抽象类,使用 LiveData
我是一名优秀的程序员,十分优秀!