Rxjava2.0+Retrofit+Okhttp(封装使用)+

作者: sweetying | 来源:发表于2018-03-25 19:19 被阅读769次

    最近看了许多关于Rxjava2.0,Retrofit及MVP相关的文章,收货颇多,于是忍不住想自己将这几天所学的东西整理一遍,便于以后看到这篇文章的人能很方便的去使用.如果这些知识还不了解的可以去看以下几篇文章:

    MVP教程

    Rxjava教程

    Retrofit教程

    MVP介绍

    • 优点
    1. 模型与视图完全分离,我们可以修改视图而不影响模型;
    2. 可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部;
    3. 我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁;
    4. 如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)。

    如下图:


    MVP与MVC对比图

    MVP实际操作

    介绍两种不同的MVP的分包方式,区别就是一种相对简单一种相对复杂

    相对使用简单的

    分包方式如下图:


    MVP架构图
    • common,一般存放一些APP常量,公用的东西
    • http,一般存放自己封装的网络请求库
    • mvp,存放,model层,presenter层和view层
    • presentation,UI展示层,一般会在这个包下在细分activity包,fragment包及自定义view包等等
    • util,App常用的一些工具类

    步骤介绍

    • 第一步 编写BasePreseter,IBaseView,及BaseActivity等基类,代码如下
      BasePresenter

       public abstract class BasePresenter<V extends IBaseView> {
      
      //Presenter持有的View
      protected V mView;
      //上下文
      protected Context mContext;
      
      //构造方法让Presenter层持有View层的引用
      public BasePresenter(Context context,V view){
          this.mContext = context;
          this.mView = view;
          }
      }
      

      IBaseView,这里我是空实现,你也可以将一些公用的方法放进去

       public interface IBaseView {  }
      

      ITestView这里边我写了一个start()方法

      public interface ITestView extends IBaseView{
                void start();
      }
      

      BaseActivity

       public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity {
        
       protected P mPresenter;
      
      @Override
      protected void onCreate(@Nullable Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(getLayoutId());
      
        initPresenter();
        initView();
      }
      
      protected abstract void initView();
      
      protected abstract void initPresenter();
      
      protected abstract int getLayoutId();
      }
      
    • 第二步 编写测试的TestActivity,及TestPreseter
      TestActivity代码很简单,就是实现ITestView以及BaseActivity的一些重写的方法

      public class TestActivity extends BaseActivity<TestPresenter> implements ITestView{
      private Context mContext;
      
      @Override
      protected void initView() {
          mPresenter.testPresenter();
      }
      
      public void testActivity(){
          Log.d("print", "拥有ITestView的引用");
      }
      
      @Override
      protected void initPresenter() {
          mContext = this;
          mPresenter = new TestPresenter(mContext,this);
      }
      
      
      @Override
      protected int getLayoutId() {
          return R.layout.activity_test;
      }
      
      @Override
      public void start() {
          testActivity();
      }
      }
      

      TestPresenter

       public class TestPresenter extends BasePresenter<ITestView>{
      
      public TestPresenter(Context mContext, ITestView mView) {
          super(mContext, mView);
      }
      
      public void testPresenter(){
          Log.d("print", "拥有TestPresenter的引用");
          mView.start();
      }
      
      }
      

      运行后的效果图如下:


      效果图

    逻辑梳理

    以上代码就是一个很简单的MVP模式,View层含有Presenter层的引用,Presenter层同时含有View层的引用,model层如涉及到一些服务,数据库等等的操作,或者从服务器获取到的数据,就可以将他的逻辑写到Presenter层里面,这里我就没演示了,这样的话,就实现了View层和model层的分离,他们都是通过Presenter层来建立联系的.

    相对复杂的

    传送门

    Rxjava+Retrofit+Okhttp封装

    • 导入以下几个包

    //Rxjava2.0使用
    compile 'io.reactivex.rxjava2:rxjava:2.0.1'
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
    // Android 支持 Retrofit
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    //okhttp日志拦截器
    compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
    // 衔接 Retrofit & RxJava
    // 此处一定要注意使用RxJava2的版本
    compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
    compile 'com.squareup.retrofit2:converter-scalars:2.0.0'
    // 支持Gson解析
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
    //Cookie持久化管理(推荐)
    compile 'com.github.franmontiel:PersistentCookieJar:v1.0.1'

    第一步

    创建工具类:RetrofitServiceManager

    RetrofitServiceManager特性
    RetrofitHelper特性:
    单例形式创建Retrofit实例;

    1. 使用okhttp3作为请求客户端;
    2. 使用gson作为数据转换器;
    3. 添加各种拦截器,如日志拦截,请求头拦截,请求参数拦截等等
    4. 开启数据缓存,无网络时可从缓存读取数据;
    5. 辅助类静态方法获取Retrofit Service实例。

    //看代码

    public class RetrofitServiceManager {
    private static final int DEFAULT_TIME_OUT = 10;//超时时间5s
    private static final int DEFAULT_READ_TIME_OUT = 10;//读取时间
    private static final int DEFAULT_WRITE_TIME_OUT = 10;//读取时间
    private Retrofit mRetrofit;
    
    
    private RetrofitServiceManager(){
        //OkHttpClient配置
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);
        builder.readTimeout(DEFAULT_READ_TIME_OUT, TimeUnit.SECONDS);
        builder.writeTimeout(DEFAULT_WRITE_TIME_OUT,TimeUnit.SECONDS);
        builder.cache(new Cache(new File(Environment.getExternalStorageDirectory() + "/RxJavaDemo"),1024*1024*10));
        //cookie持久化管理
        builder.cookieJar(new PersistentCookieJar(new SetCookieCache(),new SharedPrefsCookiePersistor(App.getInstance())));
    
        addInterceptor(builder);
    
    
        mRetrofit = new Retrofit.Builder()
                .client(builder.build())
                .baseUrl(ApiService.BASR_URL)
                .addConverterFactory(ScalarsConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
    }
    
    /**
     * 添加各种拦截器
     * @param builder
     */
    private void addInterceptor(OkHttpClient.Builder builder) {
        // 添加日志拦截器
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
    
        HttpHeaderInterceptor httpHeaderInterceptor = new HttpHeaderInterceptor.Builder().build();
        //日志拦截 
        builder.addInterceptor(loggingInterceptor);
        //头部参数拦截
        builder.addInterceptor(httpHeaderInterceptor);
        //缓存拦截
        builder.addInterceptor(new HttpCacheInterceptor());
        //请求参数拦截
        builder.addInterceptor(new CommonParamsInterceptor());
    }
    
    //单例 饿汉模式
    private static class SingletonHolder{
        private static RetrofitServiceManager retrofitServiceManager = new RetrofitServiceManager();
    }
    
    public static RetrofitServiceManager getInstance(){
        return SingletonHolder.retrofitServiceManager;
    }
    
    //获取Service实例
    public <T> T creat(Class<T> tClass){
        return mRetrofit.create(tClass);
    }
    
    }
    

    第二步

    RxJava封装,再Retrofit中使用RxJava,需要封装的地方仅有2个地方,

    1. 切换线程操作
    2. 响应结果处理:onComplete()方法可选,通用错误响应,保留onSuccess()供用户调用

    假设我们的Api返回结果如下

    {
      "code": 0 //0代表成功 非0均为失败
      "msg": ""
      "model":{}
    }
    

    为此,我们创建响应结果的基类:BaseResponse

    public class BaseResponse<T> {
    private int code;
    private String msg;
    private T model;
    
    public T getData() {
        return model;
    }
    
    public void setData(T model) {
        this.model = model;
    }
    
    public void setCode(int code) {
        this.code = code;
    }
    
    public int getCode() {
        return code;
    }
    
    public String getMsg() {
        return msg;
    }
    
    public void setMsg(String msg) {
        this.msg = msg;
    
    }
    
    public boolean isSuccess(){
        return code == 0;
    }
    
    }
    

    基于以上响应实体类,我们创建RxJava订阅者的封装基类:RxSubcriber

    public  abstract class RxSubcriber<T> implements Observer<BaseResponse<T>> {
    
    private ProgressDialog mProgressDialog;
    private Disposable disposable;
    private BaseActivity context;
    private String errorMsg;
    
    public RxSubcriber(BaseActivity context){
        this.context = context;
    }
    
    
    @Override
    public void onSubscribe(Disposable d) {
        disposable = new CompositeDisposable();
        showLoading();
    }
    
    @Override
    public void onNext(BaseResponse<T> value) {
        if(!value.isSuccess()){
            ApiException apiException = new ApiException(value.getCode(),value.getMsg());
            onError(apiException);
            return;
        }
        onSuccess(value.getData());
    }
    
    
    @Override
    public void onError(Throwable e) {
        if (e instanceof IOException) {
            /** 没有网络 */
            errorMsg = "Please check your network status";
        } else if (e instanceof HttpException) {
            /** 网络异常,http 请求失败,即 http 状态码不在 [200, 300) 之间, such as: "server internal error". */
            errorMsg = ((HttpException) e).response().message();
        } else if (e instanceof ApiException) {
            /** 网络正常,http 请求成功,服务器返回逻辑错误 */
            errorMsg = ((ApiException)e).getMsg();
        } else {
            /** 其他未知错误 */
            errorMsg = !TextUtils.isEmpty(e.getMessage()) ? e.getMessage() : "unknown error";
        }
    
    
       dismissLoading();
    
        new AlertDialog.Builder(context)
                .setTitle("提示")
                .setMessage(errorMsg)
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialogInterface.dismiss();
                        if(!disposable.isDisposed()){
                            disposable.dispose();
                        }
                    }
                }).show();
    }
    
    @Override
    public void onComplete() {
        dismissLoading();
        Log.d("print", "-->执行了完成的方法");
    }
    
    public abstract void onSuccess(T t);
    
    private void showLoading(){
        if(mProgressDialog == null){
            mProgressDialog = new ProgressDialog(context);
            mProgressDialog.setMessage("正在加载中...");
            mProgressDialog.show();
        }
    }
    
    private void dismissLoading(){
        if(mProgressDialog != null){
            mProgressDialog.dismiss();
        }
    }
    }
    

    这里主要处理了返回的数据,错误异常的统一处理,以及Dialog加载框的处理.

    接下来,对线程切换操作再做一个封装:RxJavaHelper

    public class RxjavaHelper {
    
    /**
     * 切换线程操作
     * @return Observable转换器
     */
    public static <T> ObservableTransformer<T, T> observeOnMainThread() {
        return new ObservableTransformer<T, T>() {
    
            @Override
            public ObservableSource<T> apply(Observable<T> upstream) {
                return upstream.subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }
    
    
    }
    

    基本封装完毕,现在Api的请求如下:

     RetrofitServiceManager.getInstance().creat(ApiService.class)
                        .getHome()
                        .compose(RxjavaHelper.<BaseResponse<List<RecommendEntity>>>observeOnMainThread())
                        .subscribe(new RxSubcriber<List<RecommendEntity>>(this) {
                            @Override
                            public void onSuccess(List<RecommendEntity> o) {
                                Log.d("print", "--->" + o);
                            }
                        });
    

    现在看看请求结果:


    image.png

    OK,还可以,Retrofit + RxJava2.0 封装就暂时这样了。如果使用过程有什么问题再修改修改!!
    最后附上项目Demo

    如果大家喜欢的话,请为我点赞,谢谢!

    相关文章

      网友评论

      • goldze:哥们你这样封装RxSubcriber体验不是很好
        sweetying:看个人需求,实际应用中,根据不同的情况,做做修改.
      • 码锻:哥们你的mvp模式的图画错了

      本文标题:Rxjava2.0+Retrofit+Okhttp(封装使用)+

      本文链接:https://www.haomeiwen.com/subject/xexkcftx.html