Android 优雅的封装MVP模式下的Retrofit+RxJava

前言

在刚开始学Retrofit2.0+RxJava2.0时就尝试封装起来结合MVP模式使用,虽然简化了一些请求过程,但是实际使用还是有些麻烦,而且发现其中有很大的缺陷,所以就弃用了。
随着工作到现在,接触了数个实际上线项目后,趁着工作闲暇就将其总结起来,重写之前的缺陷。
Retrofit2.0+RxJava2.0的封装过程之前已经讲过,本文也是基于这个封装的,如有问题请戳:https://blog.csdn.net/DeMonliuhui/article/details/77868677
本文就是带着大家使用MVP结合着Retrofit2.0+RxJava2.0一步步封装一个简便精简的网络请求库,主要讲解MVP的架构封装过程封装。

源码

https://github.com/DeMonLiu623/DeMon_MVPRR

使用

Android DeMon_MVPRR的使用手册

依赖

引入如下依赖:

    api 'com.squareup.okhttp3:okhttp:3.12.1'//OKHttp3.0依赖
    api 'com.squareup.okhttp3:logging-interceptor:3.10.0'//OKHttp日志优化策略
    api 'com.squareup.retrofit2:retrofit:2.5.0'//Retrofit2.0所需依赖
    api 'io.reactivex.rxjava2:rxandroid:2.0.2'//Rxandroid2.0线程调度依赖
    api 'io.reactivex.rxjava2:rxjava:2.2.5' //RxJava2.0所需依赖
    api 'com.squareup.retrofit2:converter-scalars:2.5.0'//结果转为基本类型所需依赖
    api 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'//RxJava2.0+Retrofit2.0适配依赖
    api 'com.squareup.retrofit2:converter-gson:2.3.0'//结果转为Bean类型所需依赖

使用OKhttp优化Retrofit

使用OKhttp封装一个优化通用的Retrofit。

public class BaseApi {
    //超时时长,单位:毫秒
    private int TimeOut = 7676;

    public void setTimeOut(int timeOut) {
        TimeOut = timeOut;
    }

    /**
     * 使用OkHttp配置了超时及缓存策略的Retrofit
     *
     * @param baseUrl
     * @param interceptors 自定义的拦截器
     * @return
     */
    public Retrofit getRetrofit(String baseUrl, Interceptor... interceptors) {
        HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor();
        logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        //缓存
        File cacheFile = new File(BaseApp.getContext().getCacheDir(), "cache");
        Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb
        //创建一个OkHttpClient并设置超时时间
        OkHttpClient.Builder builder = new OkHttpClient.Builder();

        builder.readTimeout(TimeOut, TimeUnit.MILLISECONDS)
                .writeTimeout(TimeOut, TimeUnit.MILLISECONDS)
                .connectTimeout(TimeOut, TimeUnit.MILLISECONDS)
                .addInterceptor(new CacheControlInterceptor())//缓存
                .addNetworkInterceptor(new CacheControlInterceptor())//网络缓存
                .cache(cache);
        if (BuildConfig.DEBUG) {
            builder.addInterceptor(logInterceptor);//日志
        }

        for (Interceptor interceptor : interceptors) {
            builder.addInterceptor(interceptor);
        }

        OkHttpClient client = builder.build();
        Retrofit retrofit = new Retrofit.Builder()
                .client(client)
                .baseUrl(baseUrl)
                .addConverterFactory(ScalarsConverterFactory.create())//请求结果转换为基本类型,一般为String
                .addConverterFactory(GsonConverterFactory.create())//请求的结果转为实体类
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//适配RxJava2.0
                .build();
        return retrofit;
    }
}

封装RxJava的Observer过程

  1. 使用泛型解决请求结果实体不同的差异性问题。
  2. 根据Observer过程使用handler控制进度dialog的显示隐藏问题。
  3. 重写BaseObserver,继承onNextResult,onErrorResult两个方法即可处理请求结果,或者处理请求错误的情况,详情见demo。
  4. 如果用户点返回即表示取消本次请求,通过onCancelProgress回调使用Disposable取消本次请求订阅,详情看DialogHandler的代码。
public abstract class BaseObserver<T> implements Observer<T>, DialogHandler.CancelListener {
    private static final String TAG = "BaseObserver";
    private DialogHandler mDialogHandler;
    private Disposable d;
    private Context mContext;
    private boolean isShow = true; //是否显示进度框
    private ProgressDialog dialog; //进度框

    public BaseObserver(Context context) {
        mContext = context;
    }

    public BaseObserver(Context context, boolean isShow) {
        mContext = context;
        this.isShow = isShow;
    }


    @Override
    public void onSubscribe(Disposable d) {
        this.d = d;
        showProgressDialog();
    }

    @Override
    public void onNext(T t) {
        onNextResult(t);//请求成功
    }

    @Override
    public void onError(Throwable e) {
        onErrorResult(e);//请求出错
        dismissProgressDialog();
    }

    @Override
    public void onComplete() {
        dismissProgressDialog();
    }

    @Override
    public void onCancelProgress() {
        //如果处于订阅状态,则取消订阅
        if (!d.isDisposed()) {
            d.dispose();
        }
    }

    protected abstract void onNextResult(T t);

    protected abstract void onErrorResult(Throwable e);


    /**
     * 请求过程中显示的进度dialog,可重写自定义
     *
     * @param context
     * @return
     */
    public ProgressDialog initDialog(Context context) {
        return new ProgressDialog(context);
    }

    /**
     * 使用handle异步及时控制dialog的显示隐藏
     */
    private void showProgressDialog() {
        if (dialog == null) {
            dialog = initDialog(mContext);
        }
        if (mDialogHandler == null) {
            mDialogHandler = new DialogHandler(mContext, dialog, this);
        }

        if (isShow) {
            mDialogHandler.obtainMessage(DialogHandler.SHOW_PROGRESS_DIALOG).sendToTarget();
        }
    }

    private void dismissProgressDialog() {
        if (mDialogHandler != null && isShow) {
            mDialogHandler.obtainMessage(DialogHandler.DISMISS_PROGRESS_DIALOG).sendToTarget();
            mDialogHandler = null;
        }
    }
}

Model层

由于Retrofit和RxJava的观察过程已经封装好,所以我们在Model中只需要简单做一些线程管理,和订阅管理即可。
将请求和取消放在子线程中,将结果返回到主线程,进行统一管理。

public class BaseModel {
    protected final String TAG = this.getClass().getSimpleName();
    public static Context mContext;

    protected <T> void addSubcription(Observable<T> ob, Observer<T> callback) {
        if (callback != null && ob != null) {
            ob.subscribeOn(Schedulers.io())
                    .unsubscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(callback);
        }
    }
}

Presenter层,View接口

封装Presenter层解决Presenter与View层的数据交互问题。

  1. View接口必须继承BaseView。
  2. BasePresenter通过BaseActivty/BaseFragment封装绑定,并通过泛型实例化,方便View层调用。
  3. 为了解决在kotlin中调用会有泛型型变的转换问题,所以BasePresenter需要继承BasePresenterInfc约束类型。
BaseView
/**
 *让所有View接口必须实现,这个接口可以什么都不做,只是用于约束类型
 */
public interface BaseView {

}
BasePresenterInfc
/**
 * Presenter层接口,约束类型。
 * 因为BasePresenter通过泛型与View接口绑定,在kotlin中调用会有泛型型变的转换问题
 * 所以该接口主要是为了解决kotlin调用的泛型型变问题
 */
public interface BasePresenterInfc {

    void setContext(Context context);

    void onStart(BaseView view);

    void onDestroy();

}
BasePresenter
public class BasePresenter<V extends BaseView> implements BasePresenterInfc {
    protected final String TAG = this.getClass().getSimpleName();
    protected V mView;
    protected Context mContext;


    /**
     * 获取V层
     */
    public V getView() {
        return mView;
    }

    @Override
    public void setContext(Context context) {
        mContext = context;
    }

    /**
     * 绑定V层
     *
     * @param view V层
     */
    @Override
    public void onStart(BaseView view) {
        mView = (V) view;
    }

    /**
     * 解绑V层
     */
    @Override
    public void onDestroy() {
        mView = null;
    }

}

View层(BaseMvpActivity/BaseMvpFragment)

BaseMvpActivity
  1. 泛型继承BasePresenterInfc,然后通过反射TUtil.getT(this)实例化。
  2. 根据生命周期绑定View,解绑View。
  3. BaseMvpFragment跟BaseMvpActivity原理一致,此处不在给出代码,详情见后文源码。
public abstract class BaseMvpActivity<T extends BasePresenterInfc> extends AppCompatActivity implements BaseView {
    protected final String TAG = this.getClass().getSimpleName();
    public Context mContext;
    protected T mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        BaseModel.mContext = this;
        mContext = this;
        initParam(getIntent());
        initLayout();
        initPresenter();
        initCreate();
    }


    /**
     * 初始化布局
     * 封装成方法的目的:
     * 例如需要实现含有标题栏的BaseActivity可重写此方法
     */
    protected void initLayout() {
        setContentView(bindLayout());
    }

    /**
     * 获取绑定的布局
     */
    protected abstract int bindLayout();

    /**
     * 获取泛型实例化Presenter
     * 绑定View
     */
    private void initPresenter() {
        mPresenter = TUtil.getT(this);
        if (mPresenter!=null){
            mPresenter.setContext(mContext);
            mPresenter.onStart(this);
        }
    }


    protected void initParam(Intent intent) {

    }

    protected abstract void initCreate();


    /**
     * 解绑View
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter!=null){
            mPresenter.onDestroy();
        }
    }
}

获取泛型实例的方法
public class TUtil {

    public static <T> T getT(Object o) {
        try {
            return ((Class<T>) ((ParameterizedType) (o.getClass()
                    .getGenericSuperclass())).getActualTypeArguments()[0]).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

总结

  1. 大量使用泛型增加了代码的灵活性,减少了实例化BasePresenter的繁琐。
  2. 本次封装Kotlin环境也完全适用,详见app Demo。
  3. 封装完毕后,只需继承实现一些自定义代码,即可使用,使用统一方便。
  4. 后面会写一篇demo使用笔记。
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页