原文: https://medium.com/@manuelvicnt/rxjava2-android-mvvm-lifecycle-app-structure-with-retrofit-2-cf903849f49e#.elz8jqnoi
一年多前,我写了一个帖子MVVM, RxJava and Retrofit。现在看, 这个帖子有点过时了。你会惊奇,一年之内你能学习多少东西。如果你回顾一下,你会对自己的代码感到尴尬。不仅是代码本身,还有你怎么到达那里的过程。对我来说,全部都好像是遗留代码。
根据新的情景和库, 我试着改进这个架构。让我们继续同一个例子(在这里获取更多信息)。这次,我将使用第一个稳定版本的Rxjava2和Retrofit。
在这篇文章中,我们将理解,在用Retrofit的MVVM架构的实际例子中, 如何使用Rxjava 2。我们也将讲到,利用网络请求响应到视图层的生命周期,怎么提高你应用的性能。
获取信息
应用结构
如果我们快速了解下不同层...
- Retrofit层: 实际上是发出网络请求
- APIService层:负责网络请求,包括解析响应,如果有必要处理它
- RequestManager层:准备将要发送的数据;链接不同的网络请求。
- ViewModel层: 处理视图层需要的逻辑
- View层:视图是哑的,只是处理用户输入
亲自动手
在这篇文章中,我将大量论述一个小项目,你将看见一切是怎么实现的
manuelvicnt/RxJava2-MVVM-Android-Structure
生命周期导致Views 和 ViewModels之间的问题?
在上个用Rxjava1的文章中,我们在ViewModels中有Subjects,响应信息到有Subscribers的Views中。当我说我在一年之中我学习了很多,你记得这部分?嗯,就是这个例子。
我们全都遇见过相同的问题:如果应用回到后台,我们不想取消网络请求,或者多次请求网络。
其中我们面对的一个问题是,Subscriber/Observer的onNext() 或者onComplete()方法被调用,但View不在屏幕中(注:即不在前台)。如果Subscriber试着回信息到视图(通过一个BusManager或者一个Callback),在那个方法中,我们试着更新任何UI的控件,那么我们的应用可能Crash。当持有信息时,Subject相当有帮助,直到View显示来获取。
如果你看一下新的代码仓库,Views 和 ViewModels之间的通信是通过一个接口(或者叫回调),我们叫它Contract。这给你提供了灵活性:在ViewModel的上面即插即用任何的View。
假设你有不同的Views,取决于你的设备是智能手机、平板电脑或者智能手表,所有这些都可能分享同一个ViewModel,但是反之不亦然(注:一个View不能有多个ViewModel)。
怎么解决生命周期的问题?
定义一个接口来每个时刻发生了什么
public interface Lifecycle {
interface View {
}
interface ViewModel {
void onViewResumed();
void onViewAttached(@NonNull Lifecycle.View viewCallback);
void onViewDetached();
}
}
View将在自己的onResume()中,调用Lifecycle.ViewModel#onViewResumed();在onStart()中调用Lifecycle.ViewModel#onViewAttached(this);在onDestroy()中调用Lifecycle.ViewModel#onViewDetached()。
这样,ViewModel清楚了生命周期,什么时候显示什么或者不显示的逻辑将移到ViewModel中(本应该这样的),所以当有信息的时候,它能有相应的响应和通知视图。
View和ViewModel之间的Contract
Contact定义了View需要从ViewModel获取了什么,反之亦然。通常,我们根据一个界面定义一个contract,尽管你也可以根据一个功能来定义。
在我们的例子中,我们有个Home界面,能够刷新User数据。我们定义我们的contract为:
public interface HomeContract {
interface View extends Lifecycle.View {
void showSuccessfulMessage(String message);
}
interface ViewModel extends Lifecycle.ViewModel {
void getUserData();
}
}
这个contract扩展了Lifecycle contract,所以ViewModel也将知道生命周期
Rxjava 2 响应流的类型
Rxjava 2中,引进了一些概念,重命名了另外一些。看下文档获取更多的信息
两者之间重要的不同是背压的处理。基本上,Flowable是能够处理背压的Observer,同样的关系连接了FlowableProcessor和Subject,Subscriber和Observer,等等。
记住,Completable、Single和Maybe不处理背压。
为了学习的目的,我们将Retrofit返回Observable对象。如果我们想处理背压呢?如果我们知道预期的结果,想通过指定想要获得的Stream来优化我们的代码呢?
使用Completable
让我们注册调用作为例子。因为RegistrationAPIService是处理这个信息的,我们不想返回Stream,因为在RequestManager层响应没有使用。我们仅仅关系这个调用是否成功。为此,我们返回Completable对象,忽略我们从Observable获取的元素。
public Completable register(RegistrationRequest request) {
return registrationAPI.register(request)
.doOnSubscribe(disposable -> isRequestingRegistration = true)
.doOnTerminate(() -> isRequestingRegistration = false)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.onErrorResumeNext(this::handleRegistrationError)
.doOnNext(registrationResponse -> processRegistrationResponse(request, registrationResponse))
.ignoreElements();
}
使用Maybe
如果我们想把response返回到RequestManager层,但是因为这是个网络请求,而且我们知道我们将收到一个对象,我们可以使用Maybe(有可能,body是空的,所以当null对象时,我们使用Maybe来避免异常)
记住,用singleElement()操作子,而不是singleElement()操作子。如果你使用第二个,你获取不到什么,它将抛一个异常,因为它一直会尝试获取第一个元素,即使没有第一个元素。
public Maybe login(LoginRequest request) {
return loginAPI.login(request.getNickname(), request.getPassword())
.doOnSubscribe(disposable -> isRequestingLogin = true)
.doOnTerminate(() -> isRequestingLogin = false)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.onErrorResumeNext(this::handleLoginError)
.doOnNext(this::processLoginResponse)
.singleElement();
}
使用Flowable
就像我们前面说的,Flowable和Observable有相同的行为,但是能够处理背压。为此,当Observable转为Flowable的时候,我们不得不指定我们想要的哪个策略。
有不同的策略:Buffer(缓冲所有onNext的值,直至下游消费它),DROP(放弃最近的onNext值如果下游不能赶上),ERROR(发出MissingBackpressureException,万一下游不能赶上)也是Observable相同的行为,LATEST(保留最新的onNext值,重写前面的值如果下游不能赶上)和MISSING(onNext事件没有任何缓冲和丢弃)。
在我们的Games例子中,我们使用BUFFER策略,因为我们不想失去任何game,万一下游不能赶上。这可能有点慢,但是所有的事件就在那里。
public Flowable getGames(GamesRequest request) {
return gamesAPI.getGamesInformation(request.getNickname())
.doOnSubscribe(disposable -> isRequestingGames = true)
.doOnTerminate(() -> isRequestingGames = false)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnError(this::handleAccountError)
.toFlowable(BackpressureStrategy.BUFFER);
}
使用Zip操作子同时进行不同的网络请求
如果你想同时不同的网络请求,仅当所有的网络请求都成功的时候,得到通知,这时,你应该使用Zip操作子。这非常强大!这是我喜欢的操作子之一。
#UserDataRequestManager.java
public Flowable