RXJava2+Retrofit2+MVP+RXLifecycle+EventBus+...之你可能需要的那些套路(一)

本文所涉及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://github.com/LegendaryMystic/HYMVP

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,710评论 4 376
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,839评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,295评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,776评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,198评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,074评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,200评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,986评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,733评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,877评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,348评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,675评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,393评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,209评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,212评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,003评论 2 280

推荐阅读更多精彩内容