本文所涉及DEMO已上传至https://github.com/LegendaryMystic/HYMVP
本人小白一个,文章废话较多,如果你觉得talk is cheap,喜欢直接 read the fuck source code,可跳过直接前往
码字不易,如果代码能够帮到你,望不吝给个鼓励的star,感谢!
RxJava问世至今其火爆程度已可见一斑,加之眼下普遍流行使用的基于OKHttp的Retrofit网络请求框架对其完美支持,使得其如王者农药一般饱受一众屌丝Android程序猿的喜爱。农药玩的人多了,你就不得不掌握一些骚套路,才能上你们最爱的“王者”。
本文正是本着学习的态度,也谈一谈RxJava+Retrofit结合MVP架构模式在实际项目中你可能需要的一些基本的骚操作。
在此之前,如果你还不了解RxJava请传送至扔物线的给 Android 开发者的 RxJava 详解;如果你还没使用过Retrofit,那....请自便,看Retrofit官网吧,或者你需要我教你请点这里.
也谈MVP
对于MVP,全称Model-View-Presenter,众所周知它是从经典的MVC模式演变而来的。
mvp.jpg在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。
在MVC里,View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示,即View。所以,在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。
虽然 MVC 中的 View 的确“可以”访问 Model,但是我们不建议在 View 中依赖 Model,而是要求尽可能把所有业务逻辑都放在 Controller 中处理,而 View 只和 Controller 交互。
总结一下,在MVP里,Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。模型与视图完全分离,我们可以修改视图而不影响模型,而且,Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以保持Presenter的不变,即重用! 不仅如此,我们还可以编写测试用的View,模拟用户的各种操作,从而实现对Presenter的测试(单元测试)
套路一:订阅和取消订阅问题
RxJava基于观察者模式,一般当我们借助RxJava请求网络数据时,需要网络返回数据后更新UI,此时如果视图已经消亡,则需要在对应的生命周期取消订阅,否则会导致内存泄漏。
简单的,我们在视图消亡后,无需RxJava再执行,可以直接取消订阅
if (!subscription.isUnsubscribed()) {
subscription.unsubscribe();
}
observable.unsubscribeOn(Schedulers.io());
可用在activity的 onDestroy()
, Fragment的 onDestroyView()
中调用。
那在MVP模式中,只要我们在Presenter在基类中定义一个CompositeDisposable容器,将请求订阅disaposable添加到容器里统一管理,必要时clear即可。
protected CompositeDisposable mCompositeDisposable;
/**
* 将 {@link Disposable} 添加到 {@link CompositeDisposable} 中统一管理
* 可在 {@link Activity#onDestroy()} 中使用 {@link #unDispose()} 停止正在执行的 RxJava 任务,避免内存泄漏
* 目前已使用 {@link RxLifecycle} 避免内存泄漏,此方法作为备用方案
*
* @param disposable
*/
protected void addDisposabel(Disposable disposable) {
if (mCompositeDisposable == null) {
mCompositeDisposable = new CompositeDisposable();
}
//将所有 Disposable 放入集中处理
mCompositeDisposable.add(disposable);
}
public void unDispose(){
if (mCompositeDisposable != null) {
mCompositeDisposable.clear();//保证 Activity 结束时取消所有正在执行的订阅
}
}
这里我们采用另一种方案:
套路二: RxLifecycle + MVP
如果你有心不难发现Github上已有人对RxJava 管理订阅的问题作出了贡献:RxLifecycle
Github: https://github.com/trello/RxLifecycle
常规的,RxLifecycle使用很简单,只需要集成rxlifecycle-components
组件,Components包中包含RxActivity、RxFragment等等,可以用Rxlifecycle提供的,也可以自定义。
让你的BaseActivity
继承RxAppCompatActivity
,然后像这样给你的Observable绑定lifecycle
myObservable
.compose(RxLifecycle.bind(lifecycle))
.subscribe();
或者你也可以指定bind特定生命周期事件:
myObservable
.compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
.subscribe();
然而这都不是重点,重点是如何在MVP模式里使用呢?我的答案很简单:
在View的接口基类里增加一个接口:
public interface BaseView {
<T> LifecycleTransformer<T> bindToLifecycle();
/**
* 显示加载
*/
void showLoading();
/**
* 隐藏加载
*/
void hideLoading();
}
而你的Activity基类BaseActivity
继承自RxAppCompatActivity
自然也就实现了<T> LifecycleTransformer<T> bindToLifecycle();
这个接口方法,这样我们只需要在Presenter里使用的时候像上面一样给Observable bindToLifecycle即可。这个的封装代码将在下一个套路讲解一并展示,看完你会恍然大悟。
接下来,如你所见,我们的BaseActivity
基类直接继承RxLifecycle的RxAppCompatActivity
,那么问题来了,也许你以后还需要使用一些其他的第三方,而它又需要你继承它的Activity,然而Java是没有多继承的,显然这样还不够完善。
此时,如果你稍有Read the fucking Source code,这里附上RxAppCompatActivity
的源码
public abstract class RxAppCompatActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {
private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
@Override
@NonNull
@CheckResult
public final Observable<ActivityEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindActivity(lifecycleSubject);
}
@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycleSubject.onNext(ActivityEvent.CREATE);
}
@Override
@CallSuper
protected void onStart() {
super.onStart();
lifecycleSubject.onNext(ActivityEvent.START);
}
@Override
@CallSuper
protected void onResume() {
super.onResume();
lifecycleSubject.onNext(ActivityEvent.RESUME);
}
@Override
@CallSuper
protected void onPause() {
lifecycleSubject.onNext(ActivityEvent.PAUSE);
super.onPause();
}
@Override
@CallSuper
protected void onStop() {
lifecycleSubject.onNext(ActivityEvent.STOP);
super.onStop();
}
@Override
@CallSuper
protected void onDestroy() {
lifecycleSubject.onNext(ActivityEvent.DESTROY);
super.onDestroy();
}
}
不难看出,它里面就是创建了一个BehaviorSubject 对象,BehaviorSubject是Subject的子类,首先我们来回顾一下这个Subject对象:
如何理解Subject呢?
在RxJava里面,Observable是数据的发射者,它会对外发射数据,然后经过map、flatmap等等数据处理后,最终传递给Observer,这个数据接收者。因此,抛开中间数据处理不管,可以看出,Observable对外发射数据,是数据流的开端;Observer接收数据,是数据流的末端。
那么Subject呢?看一眼源码:
/**
* Represents an {@link Observer} and an {@link Observable} at the same time, allowing
* multicasting events from a single source to multiple child {@code Observer}s.
* <p>
* All methods except the {@link #onSubscribe(io.reactivex.disposables.Disposable)}, {@link #onNext(Object)},
* {@link #onError(Throwable)} and {@link #onComplete()} are thread-safe.
* Use {@link #toSerialized()} to make these methods thread-safe as well.
*
* @param <T> the item value type
*/
public abstract class Subject<T> extends Observable<T> implements Observer<T> {}
首先,它extends Observable,说明Subject具备了对外发射数据的能力,即拥有了from()、just()等等;另外,它又implements Observer,说明又能够处理数据,具备onNext()、onCompleted等等。
然后通过这个subject对象监听Activity生命周期事件然后再发射出去。我们的observable调用bindToLifecycle
时就实现了通过subject监听并转发射Activity的生命周期事件,比如我我们绑定了Activity的destroy事件,当我们的observable收到了这个destroy事件就过滤掉不再传下去,做后面的UI绘制操作。
那其实,说了这么多,最简单粗暴的解决方法就是必要时将上述代码copy到你的BaseActivity
里即可_
套路三: Scheduler + ObservableTransformer 线程调度
RxJava其中一个牛逼之处就在于其提供了便利自由的线程控制,然而在使用RxJava配合进行网络请求时,你是否会发现你经常需要频繁得敲如下代码:
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
我这里用了一个工具方法便将上述绑定 Rxlifecycle 一并封装了:
/**
* 界面请求,不需要加载和隐藏loading时调用 使用RxLifeCycle
* 传入view接口,Activity,Fragment等实现了view接口,Activity,Fragment继承于{@link com.trello.rxlifecycle2.components.support.RxAppCompatActivity}
* 也就实现了bindToLifecycle方法
* @param view View
* @param <T> 泛型
* @return
*/
public static <T> ObservableTransformer<T, T> transform(final BaseView view) {
return observable -> observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(view.bindToLifecycle());
}
使用时只需利用compose操作符调用即可。废话有点多了,详情请见源代码HYMVP由于篇幅问题,后续诸多套路,请听下回分解。
网友评论
https://www.jianshu.com/p/df2a6717009d