RxJava+Retrofit2+MVP实现网络请求

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_435559203/article/details/52690196

上一遍博客介绍了RxJava+Retrofit2的使用。在前段时间,刷招聘简历的时候,发现有一部分的公司会要求MVP模式的理解和具体使用。在现在越来越复杂的业务,我们的Activity的负担也是越来越大,因此接着这篇我结合MVP模式来介绍一下自己对RxJava+Retrofit2+MVP的使用。

MVP的理解

RxJava+Retrofit2+MVP三者结合使用

MVP的理解

因为我相信大家在开发App时,肯定会发现,Activity的负担非常重,既要初始化控件,又要写一些逻辑操作的展示等等,有时候很多Activity中的代码都充当了Controller和Model的角色,所以你会发现Activity违背单一职责原则,负担过重。所以,就出现了这么一种架构模式,叫MVP。
而当将架构改为MVP以后,Presenter的出现,将Actvity视为View层,Presenter负责完成View层与Model层的交互。现在是这样的:

  • 视图(View)对应于Activity,负责View的绘制以及与用户交互
  • 模型(Model)依然是业务逻辑和实体模型
  • 主持人(Presenter)相当于协调者,是模型与视图之间的桥梁,负责完成View于Model间的交互,将模型与视图分离开来。

借下图一用,View与Model并不直接交互,而是使用Presenter作为View与Model之间的桥梁。其中Presenter中同时持有 Viwe层以及Model层的Interface的引用,而View层持有Presenter层Interface的引用。当View层某个界面需要展示 某些数据的时候,首先会调用Presenter层的某个接口,然后Presenter层会调用Model层请求数据,当Model层数据加载成功之后会调 用Presenter层的回调方法通知Presenter层数据加载完毕,最后Presenter层再调用View层的接口将加载后的数据展示给用户。这 就是MVP模式的整个核心过程。
这里写图片描述
这样分层的好处就是大大减少了Model与View层之间的耦合度。一方面可以使得View层和Model层单独开发与测试,互不依赖。另一方面 Model层可以封装复用,可以极大的减少代码量。当然,MVP还有其他的一些优点,这里不再赘述。
但是你可以发现这里超级多累简直就是类数量爆炸,代码复杂度和学习成本高,还有在某些场景下presenter的复用会产生接口冗余。所以MVP模式是不是适合你使用?在使用之前需要思考一下是否有必要对自己的项目动刀?

RxJava+Retrofit2+MVP三者结合使用

首先看看我们用到的库

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:24.1.0'
    compile 'com.google.code.gson:gson:2.7'
    compile 'com.jakewharton:butterknife:7.0.1'
    compile 'com.squareup.okhttp3:okhttp:3.3.0'
    compile 'com.squareup.okhttp3:logging-interceptor:3.3.0'
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
    compile 'io.reactivex:rxjava:1.1.0'
    compile 'io.reactivex:rxandroid:1.1.0'
}

再看看包目录结构,相信有部分的同学曾纠结应该如何怎么布置自己的项目目录结构,可以参考一下我的方式;
这里写图片描述

View:用户在登录界面交互触发的事件,我们需要处理到的方法。这里需要我们想象一下在我们的Activity中一共会出现的交互有哪些。

public interface IUserLoginView {
    void loginSuccess();

    void loginFail(int toast, String reason);

    void showLoading();

    void hideLoading();

    void saveCommonUserInfo(UserInfo info);
}

负责登录的Activity

public class LoginActivity extends MvpActivity<UserLoginPresenter> implements IUserLoginView {
    private Context mContext;
    @Bind(R.id.editText_name)
    EditText mEtGwNumber;
    @Bind(R.id.editText_pwd)
    EditText mEtPassword;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        mContext = this;
    }

    @Override
    protected UserLoginPresenter createPresenter() {
        return new UserLoginPresenter(this);
    }

    @OnClick(R.id.button_login)
    public void onClickLogin() {
        Map<String, String> maps = new HashMap<>();
        maps.put("name", mEtGwNumber.getText().toString());
        maps.put("pwd", mEtPassword.getText().toString());
        mvpPresenter.login(maps);
    }

    @Override
    public void loginSuccess() {
        Intent intent = new Intent(LoginActivity.this, MainActivity.class);
        startActivity(intent);
    }

    @Override
    public void loginFail(int toast, String reason) {
        if (reason.isEmpty()) {
            Toast.makeText(LoginActivity.this, getString(toast), Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(LoginActivity.this, reason, Toast.LENGTH_LONG).show();
        }
    }

    @Override
    public void showLoading() {
        DialogUtils.getInstance().popRemindDialog(mContext, "正在登录");
    }

    @Override
    public void hideLoading() {
        DialogUtils.getInstance().disMissRemind();
    }

    @Override
    public void saveCommonUserInfo(UserInfo info) {
        //这里将登录接口返回的数据,根据实际情况存储在SP或者SQLite
    }
}

这里会继承一个MvpActivity,管理Presenter对Activity的生命周期,防止Presenter持有Activity对象可能导致的内存泄漏问题。

public abstract class MvpActivity<P extends BasePresenter> extends BaseActivity {
    protected P mvpPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        mvpPresenter = createPresenter();
        super.onCreate(savedInstanceState);
    }

    protected abstract P createPresenter();

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mvpPresenter != null) {
            mvpPresenter.detachView();
        }
    }
}

用户点击登录按钮,实现IUserLoginModel接口,传入一个Map集合对象,返回Subscription类型

public interface IUserLoginModel {
    Subscription login(Map<String, String> maps, OnLoginListener listener);
}

对IUserLoginModel接口进行结果监听

public interface OnLoginListener {
    void loginSuccess(UserInfo userInfo);

    void loginFailed(int toast, String reason);

    void requestCompleted();
}

通过UserLoginModel去实现IUserLoginModel接口,结合RxJava+Retrofit2实现。这里的具体内容可以参数我上篇博客。

public class UserLoginModel implements IUserLoginModel {
    @Override
    public Subscription login(Map<String, String> maps, final OnLoginListener listener) {
        Observable<JSONObject> observable = RetrofitManage.getInstance().getHttpServiceConnection().login(maps);
        return observable.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io()) //在io线程中处理网络请求
                .observeOn(AndroidSchedulers.mainThread()).subscribe(new SubscriberCallBack<>(new ApiCallback<JSONObject>() {
                    @Override
                    public void onSuccess(JSONObject model) {
                        UserInfo userInfo = JsonHandleAdapter.getUserLoginInfo(model.toString());
                        listener.loginSuccess(userInfo);
                    }

                    @Override
                    public void onFailure(int msg, String reason) {
                        listener.loginFailed(msg, reason);
                    }

                    @Override
                    public void onCompleted() {
                        listener.requestCompleted();
                    }
                }));
    }
}

到View和Model的中转站Presenter,这里将Model返回的结果进行判断处理。

首先Presenter的基类BasePresenter

public class BasePresenter<V> implements Presenter<V> {
    public V mvpView;
    private CompositeSubscription mCompositeSubscription;

    @Override
    public void attachView(V view) {
        this.mvpView = view;
    }

    @Override
    public void detachView() {
        this.mvpView = null;
        onUnSubscribe();
    }

    //RxJava取消注册,以避免内存泄露
    public void onUnSubscribe() {
        if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
            mCompositeSubscription.unsubscribe();
        }
    }


    public void addSubscription(Subscription subscription) {
        if (mCompositeSubscription == null) {
            mCompositeSubscription = new CompositeSubscription();
        }
        mCompositeSubscription.add(subscription);
    }
}

泛型Presenter:

public interface Presenter<V> {
    void attachView(V view);

    void detachView();
}

最终我们的UserLoginPresenter登录Presenter,我们这里登录Presenter,简化了不少,但是具体思路是这样。

public class UserLoginPresenter extends BasePresenter<IUserLoginView> {
    private IUserLoginModel iUserLoginModel;

    public UserLoginPresenter(IUserLoginView view) {
        attachView(view);
        iUserLoginModel = new UserLoginModel();
    }

    public void login(Map<String, String> maps) {
        mvpView.showLoading();
        addSubscription(iUserLoginModel.login(maps, new OnLoginListener() {
            @Override
            public void loginSuccess(UserInfo userInfo) {
                mvpView.loginSuccess();
                if (null != userInfo)
                    mvpView.saveCommonUserInfo(userInfo);
            }

            @Override
            public void loginFailed(int toast, String reason) {
                mvpView.loginFail(toast, reason);
            }

            @Override
            public void requestCompleted() {
                mvpView.hideLoading();
            }
        }));
    }
}

注意上述代码,我们的Presenter完成View和Model的交互。首先我们在View接收到用户的操作,我们通过实现Presenter的登录方法,在Model的登录方法中完成耗时操作网络请求,请求后的结果回调给Presenter,然后Presenter在调用View的对象,执行View对应的方法。

总结:
这里小小的登录接口,就需要到很多实现接口类,所以在我们的实际情况中,到底要不要全部采用MVP模式呢?需要大家具体情况具体分析。不要盲目认为MVP模式好厉害,好整洁就使用,毕竟我们需要保证我们的程序正常运行,代码如期迭代,其他的同学看你的代码不会晕掉。
另外我的封装方式不一定适用你,但是这个方式是在我线上的项目中已经实行了。不过我感觉还是需要继续迭代、继续优化、继续学习。谢谢大家!

源码点击下载:

这里写图片描述

阅读更多

扫码向博主提问

firejunking

非学,无以致疑;非问,无以广识
  • 擅长领域:
  • Android优化
去开通我的Chat快问

没有更多推荐了,返回首页