一时兴起的MVP+RxJava+Retrofit的封装录

MVP介绍:

MVP 全称:Model-View-Presenter ;MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。

RxJava介绍:

RxJava 在 GitHub 主页上的自我介绍是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库)。这就是 RxJava ,概括得非常精准。对RxJava不熟悉的可以看下https://gank.io/post/560e15be2dca930e00da1083这篇文章(我也不是很熟悉,拿来就用而已,手动摊手)。

Retrofit介绍:

Retrofit 其实相当简单,简单到源码只有37个文件,其中22个文件是注解还都和HTTP有关,真正暴露给用户的类并不多,所以我看了一遍 官方教程 大多数情景就可以无障碍使用,如果你还没有看过,可以先去看看,虽然是英文,但代码才是最好的教程不是么?当然本篇文章会介绍得详细一点,不能写一篇水文,毕竟我给它命名为《你真的会用Retrofit2吗?Retrofit2完全教程》。附上链接  https://www.jianshu.com/p/308f3c54abdd
 
进入正题吧,我们首先来尝试搭建一个MVP框架:
1、从View开始,新建一个BaseView接口,定义一下View层通用的方法;
public interface BaseView {
    /**
     * 显示正在加载view
     */
    void showLoading();
    /**
     * 关闭正在加载view
     */
    void hideLoading();
    /**
     * 显示提示
     * @param msg
     */
    void showToast(String msg, int length);
    /**
     * 显示请求错误提示
     */
    void showErr(String errMsg);
    /**
     * 获取上下文
     * @return 上下文
     */
    Context getContext();
}

比如现在有个登录需求需要实现,新建一个LoginView,并继承BaseView,使各功能View能自行实现自己的需求;

public interface LoginView extends BaseView {

    void loginSuccess();

    void loginFailure();

}

OK,我们现在有了登录模块的View了,那么,我们可以在相关登录模块的Activitiy,Fragment上实现该LoginView,例如:

public class LoginActivity extends BaseActivity<LoginPresenter> implements LoginView{
    @Override
    public void loginSuccess() { } 

    @Override 
    public void loginFailure() { } 
}

好的,那么我们的LoginActivity就实现了我们的LoginView功能了,BaseActivity待会会介绍;

View大概就这么多,可以根据具体的功能需求添加删除;

2、Presenter层:由上文提到的,负责逻辑处理;

同样,新建一个BasePresenter类,因为presenter层级的操作是基于View层的,我们需要在BasePresenter每一步操作前判断我们的view是否存在;

public class BasePresenter<V extends BaseView> {

    private V view;

    /**
     * 绑定view,一般在初始化中调用该方法
     */
    public void attachView(V view){
        this.view = view;
    }

    /**
     * 断开view,一般在onDestroy中调用
     */
    public void detachView(){
        this.view = null;
    }

    /**
     * 是否与View建立连接
     * 每次调用业务请求的时候都要出先调用方法检查是否与View建立连接
     */
    public boolean isViewAttached(){
        return view != null;
    }

    /**
     * 获取连接的view
     */
    public V getView(){
        return this.view;
    }

    public Context getContext(){
        return this.view.getContext();
    }

}

如上图,我们可以通过isViewAttached判断view是否存在,如果view已销毁,我们的逻辑处理也没必要继续下去,进行相对应的销毁工作即可;

OK,同样我们来实现功能Presenter,按照View的现在,我们也实现一个登录模块的Presenter,新建一个LoginPresenter抽象类,并将对应的View以泛型参数声明:

public abstract class LoginPresenter extends BasePresenter<LoginView> {

    /**
     * 登录方法
     */
    public abstract void login(String phoneNum, String password);

}

然后,我们将具体实现LoginPresenter,新建一个LoginPresenterImpl类,并继承LoginPresenter;

public class LoginPresenterImpl extends LoginPresenter {

    @Override
    public void login(String phoneNum, String password) {
        if (isViewAttached()) {
            // 实现Login功能
        }
    }
}

Ok,现在我们回头来看LoginActivity,将我们对应功能Presenter以泛型参数传入

LoginActivity extends BaseActivity<LoginPresenter>

3、来看看BaseActivity,直接上代码:

public abstract class BaseActivity<T extends BasePresenter> extends FragmentActivity implements BaseView {

    protected T mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setupContentView();
        initView();
        createPresenterAndAttachView();
    }

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

    @Override
    public void showLoading() {
        Views.getInstance().showTipDialog(Views.TIPDIALOG_LOADING, this);
    }

    @Override
    public void hideLoading() {
        Views.getInstance().closeTipDialog();
    }

    @Override
    public void showToast(String msg, int length) {
        runOnUiThread(() -> Toast.makeText(getContext(), msg, length).show());
    }

    @Override
    public void showErr(String errMsg) {
        showToast(errMsg, Toast.LENGTH_LONG);
    }

    @Override
    public Context getContext() {
        return BaseActivity.this;
    }

    protected abstract void createPresenterAndAttachView();

    protected abstract void setupContentView();

    protected abstract void initView();
}

BaseActivity实现BaseView,将公共功能实现一下,同时我们的Presenter在onDestroy()上将view解绑,同时我也写了几个抽象方法,createPresenterAndAttachView()在该方法中创建Presenter并进行view的连接工作,

其他就可有可无啦,根据自己的想法走吧。

好了,我们现在差不多可以用起来了。来看看LoginActivity的具体实现:

public class LoginActivity extends BaseActivity<LoginPresenter> implements LoginView, View.OnClickListener {

    @Override
    protected void createPresenterAndAttachView() {
        // 实例化Presenter
        mPresenter = new LoginPresenterImpl();
        // view建立连接
        mPresenter.attachView(this);
    }

    @Override
    protected void setupContentView() {
        setContentView(R.layout.activity_login);
    }

    @Override
    protected void initView() {
        // 初始化界面
    }

    @Override
    public void loginSuccess() {
        // 登录成功
    }

    @Override
    public void loginFailure() {
        // 登录失败
    }

    @Override
    public void onClick(View view) {

    }
}
    

4、上面似乎还少了个Model层,Model层主要是负责数据,如网络访问,数据库查询等操作,该层没有进一步进行封装了,先来看看Presenter层里是怎样跟Model层交互的;

public class LoginPresenterImpl extends LoginPresenter {

    @Override
    public void login(String phoneNum, String password) {
        if (isViewAttached()) {
            Map<String, Object> params = new HashMap<>();
            params.put("phoneNum", phoneNum);
            params.put("password", password);
            UserModel.login(getContext(), params, new Callback<LoginResponse>() {
                @Override
                public void onSuccess(LoginResponse data) {

                }

                @Override
                public void onFailure(int errCode, String msg) {

                }
            });
        }
    }

}

如上图,在Presenter具体的逻辑实现中,我们只是简单的调用了Model层进行具体的数据操作;

让我们看看UserModel是怎样做的:

/**
 * 用户Model
 */
public final class UserModel {

    /**
     * 登录
     * @param params
     * @param callback
     */
    public static void login(Context context, Map<String, Object> params, Callback<LoginResponse> callback) {
        RetrofitManager.getInstance().createService(UserApi.class).login(params)
                .compose(RetrofitManager.getInstance().ioMainSchedulers())
                .subscribe(new BaseObserver<>(context, callback));
    }
}

LoginResponse是服务端返回的实体数据,具体如何造就看你服务端具体返回什么了;

Callback是一个通用的接口,如下:

public interface Callback<T> {

    /**
     * 数据请求成功
     *
     * @param data
     */
    void onSuccess(T data);

    /**
     * 使用网络API接口请求方式时,虽然已经请求成功但是由
     * 于{@code msg}的原因无法正常返回数据。
     */
    void onFailure(int errCode, String msg);
}

5、终于到RxJava和Retrofit的使用了,RetrofitManager是一个Retrofit管理类

/**
 * Retrofit管理类
 */
public final class RetrofitManager {


    private Retrofit retrofit;

    public static RetrofitManager getInstance() {
        return RetrofitHelper.manager;
    }

    private RetrofitManager() {
        retrofit = new Retrofit.Builder()
                .baseUrl(HttpConstants.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
    }

    private static class RetrofitHelper {
        private static final RetrofitManager manager = new RetrofitManager();
    }

    public <T> T createService(Class<T> clazz) {
        return retrofit.create(clazz);
    }

    public <T> ObservableTransformer<T, T> ioMainSchedulers() {
        return upstream -> upstream.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }
}

主要就是实现一个单例模式,然后配合RxJava进行使用,如下login接口:

public interface UserApi {

    @FormUrlEncoded
    @POST(HttpConstants.LOGIN)
    Observable<BaseResponse<LoginResponse>> login(@FieldMap Map<String, Object> params);
}

Retrofit+RxJava主要是接口返回参数为RxJava的Observable,就是这么简单,深入的我也还没去研究,先用起来吧。

Ok,大概就是这样了。这样的话,我们的各层级进行了应有的解耦,代码也干净了很多。但是,代码量我觉得是多了的。

自己随手记录的一些内容,相当于是复习了下RxJava,Retrofit的使用和Mvp的概念,不喜勿喷。

猜你喜欢

转载自www.cnblogs.com/Jhon-Mr/p/9628828.html