上下文
我使用Retrofit2和RxJava来处理服务器端响应的Android应用程序。服务器端知道如何处理etags,因此服务器可以将请求响应返回为200(成功)或304(未修改)。问题是我们希望能够在Subscriber#onNext()
中以不同方式处理这些响应。例如,我们有一个用SQLlite实现的本地数据库,所以当服务器端请求返回200时,我们希望将该响应存储在我们的本地数据库中,但如果请求响应返回304,我们不想这样做,因为它与我们已存储在数据库中的数据相同,并且浪费时间再次保存它。
我们正在使用Fernando Cejas Clean Architecture作为我们的应用架构。
我们的Interactors正在使用Repositories,它使用Retrofit层来执行服务器端请求并返回rx.Observable<List<SomeModelClass>>
之类的数据。我们的Interactors也使用返回数据库数据的DAO,如rx.Observable<List<SomeModelClass>>
。因此,通过这样做,我们可以做一些非常酷的事情,例如:
public void GetDogsInteractor {
...
public void execute (Callback callback) {
mDogsRepository.getAllDogs()
.onErrorNext(ObservableUtils.ifExceptionIs(SomeServerSideException.class, mDogsDao.getDatabaseDogs()))
.subscribe(
new Subscriber<List<Dog>>() {
...
public void onError() {
callback.showError("Sorry no doggos on server-side nor database");
}
public void onNext(List<Dog> dogs) {
callback.showDogsOnUI(dogs);
}
...
}
);
}
...
}
正如您所看到的,我们的存储库和我们的DAO具有相同的接口,可以很容易地将其与另一个插入,以防出现问题。
解决方法:
让我们想象一下,只有当请求响应为200时,我们才想将所有Dog对象存储在数据库中,但如果请求响应是304,我们就不会这样做。我唯一能解决的问题是如果响应是304,则过滤服务器端响应并抛出异常,这样我就可以在Subscriber#onNext()
和Subscriber#onError()
中的304s处理200个请求。这就是Repository内部实现的外观:
public class DogRepository {
...
public rx.Observable<List<Dog>> getAllDogs() {
mRetrofitService.getAllDogsFromServerSide() //Returns rx.Observable<Response<List<Dog>>>
.flatMap(new Func1<Response<List<Dog>>, Observable<List<Dog>>>() {
@Override
public Observable<List<Dog>> call(Response<List<Dog>> serverSideResponse) {
//Check the response code, if its 200 just pass the response through, if its 304 throw an exception
if (serverSideResponse.getCode() == 304) {
return Observable.error(new NotModifiedException(serverSideResponse.getBody()));
} else {
return Observable.just(serverSideResponse.getBody());
}
}
})
}
...
}
然后我创建一个覆盖Subscriber#onError()
方法的自定义订阅者:
public class MySubscriber<T> extends rx.Subscriber<T> {
...
public void onError(Throwable e) {
if (e instance of NotModifiedException) {
onNotModified(((NotModifiedException) e).getBody()); //onNotModified() is a hook method
}
}
public void onNotModified(T model) {
//Optional to implement
}
...
}
然后我最终以这种方式使用它:
public void GetDogsInteractor {
...
public void execute (Callback callback) {
mDogsRepository.getAllDogs()
.onErrorNext(ObservableUtils.ifExceptionIs(SomeServerSideException.class, mDogsDao.getDatabaseDogs()))
.subscribe(
new MySubscriber<List<Dog>>() {
...
public void onError() {
callback.showError("Sorry no doggos on server-side nor database");
}
public void onNotModified(List<Dog> dogs) {
callback.showDogsOnUI(dogs);
}
public void onNext(List<Dog> dogs) {
mDogDao.saveAllDogs(dogs)
callback.showDogsOnUI(dogs);
}
...
}
);
}
...
}
这样我甚至可以使用onErrorNext()
使用NotModifiedException挂钩其他一些流。
问题:
我的一些同事并不乐意处理这种抛出异常并将304响应处理为某些意外的&#34;意外&#34;。不能责怪他们,这不是最好的解决方案。但我尝试了很多东西,这是唯一有效的方法。我的问题是;有没有办法在不使用例外的情况下实现这一目标?一些更清洁的方式?
我尽可能地简化了这一点,但请告诉我是否有你不能得到的东西。
答案 0 :(得分:1)
我使用Data<T>
包装类在应用程序的不同层之间传递东西。对于你的情况,我会有类似的东西:
public class Data<T> {
public static final int LOADING = 0;
public static final int SUCCESS = 1;
public static final int NO_CHANGE = 2;
public static final int ERROR = 3;
public final int status;
public final T result;
public final DataError error;
public Data(int status, T result, DataError error) {
this.status = status;
this.result = result;
this.error = error;
}
}
你甚至可以添加静态语法糖构造函数:
public static <R> Data<R> success(R result) {
return new Data<>(SUCCESS, result, null);
}
public static <R> Data<R> noChange(R result) {
return new Data<>(NO_CHANGE, result , null);
}
然后只需更改Repository
方法的返回类型即可使用Data
而不只是map
而不是flatMap
:
public Observable<Data<List<Dog>>> getAllDogs() {
mRetrofitService.getAllDogsFromServerSide()
.map(new Func1<Response<List<Dog>>, Data<List<Dog>>>() {
@Override
public Data<List<Dog>> call(Response<List<Dog>> serverSideResponse) {
if (serverSideResponse.getCode() == 304) {
return Data.noChange(serverSideResponse.getBody());
} else {
return Data.success(serverSideResponse.getBody());
}
}
})
}
现在,在您的Interactor
onNext
中,您只需对状态进行简单检查即可:
public void onNext(Data<List<Dog>> data) {
if (result.status == SUCCESS)
mDogDao.saveAllDogs(data.result)
callback.showDogsOnUI(data.result);
return;
}
if (result.status == NO_CHANGE) {
callback.showDogsOnUI(data.result);
return;
}
if (result.status == ERROR) {
//...
}
}
使用Data
包装器类,您仍然可以通过应用程序的不同层传递所需的元数据,同时仍然不会泄漏实现细节。