天天看点

优雅的使用MVP+RxJava+Retrofit框架

优雅的使用MVP+RxJava+Retrofit框架

前面我已经写过一篇关于Google的RxJava版MVP解读的文章,本篇就是我在此基础上进行了修改和封装,稍微变化一下,见上图。目的是让这个框架更加实用,同时简化操作,少写代码。果然程序员都是喜欢偷懒的- -

前言

首先,阅读本篇文章前,建议你先去了解一下MVP这个设计模式。当然,也可以先看看我前面的文章^_^ 传送门

另外,还用到了RxJava、Retrofit、Okhttp。如果你已经了解了他们的基本用法,请直接忽略这段,接着往下看~

不想看长长的文章的,可以直接看代码。代码地址已经上传到Github,https://github.com/backkomyoung/MVP

工程结构

按照惯例,先看看工程的主要结构:

优雅的使用MVP+RxJava+Retrofit框架

简单说一下几个主要包下的功能。首先是api包,这是存放对Retrofit进行包装的类。Base包当然是放各种Base类啦~ mvp包是将契约类Contract、Model的实现类和Presenter的实现类放一起,方便管理。其实你也可以按功能分包,个人喜好吧。ui包放一些界面的类,如Activity和Fragment。

下面正式开始~

契约类

同样也是从Contract契约类开始说起:

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
     
     
      10
     
     
      11
     
     
      12
     
     
      13
     
     
      14
     
     
      15
     
     
      16
     
     
      17
     
     
      18
     
     
      19
     
     
      20
     
     
      21
     
     
      22
           
public 
      interface MainContract {
     
     
         
      interface View extends BaseView {
     
     
             
      void showDialog();
     
     
             
      void onSucceed(Gank data);
     
     
             
      void onFail(String err);
     
     
             
      void hideDialog();
     
     
     
          }
     
     
         
      interface Model extends BaseModel {
     
             
      Observable<Gank> getGank();
     
     
          }
     
     
         
      abstract 
      class Presenter extends BasePresenter<View, Model> {
     
             
      public abstract void getGank();
     
     
          }
     
     
      }
           

我们可以看到,整体上和Google的Demo差不多,都是把View和Presenter放到Contract类里面统一管理,我这里多加了个Model接口,我不推荐在Presenter进行Model操作,本来很优雅的一件事,在Presenter进行Model操作的话,感觉就差了很多,要做一个优雅的程序员。不同的地方是Model和View接口继承了BaseModel接口和BaseView接口,Presenter变成了一个抽象类,继承于BasePresenter抽象类,传入两个泛型View、Model。为啥呢?我们接着看Base包下的三个Base类。

Base类

BaseModel:

1
     
     
      2
           
public 
      interface BaseModel {
     
     
      }
           

BaseView:

1
     
     
      2
           
public 
      interface BaseView {
     
     
      }
           

BasePresenter:

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
     
     
      10
     
     
      11
     
     
      12
     
     
      13
     
     
      14
     
     
      15
     
     
      16
     
     
      17
     
     
      18
     
     
      19
     
     
      20
     
     
      21
     
     
      22
     
     
      23
     
     
      24
           
public 
      class BasePresenter<V extends BaseView, M extends BaseModel> {
     
     
         
      protected V mView;
     
         
      protected M mModel;
     
     
         
      private CompositeSubscription mCompositeSubscription;
     
     
         
      protected void addSubscribe(Subscription subscription) {
     
             
      if (mCompositeSubscription == 
      null) {
     
     
                  mCompositeSubscription = 
      new CompositeSubscription();
     
     
              }
     
     
              mCompositeSubscription.add(subscription);
     
     
          }
     
     
         
      public void unSubscribe() {
     
             
      if (mView != 
      null) {
     
     
                  mView = 
      null;
     
     
              }
     
             
      if (mCompositeSubscription != 
      null && mCompositeSubscription.hasSubscriptions()) {
     
     
                  mCompositeSubscription.clear();
     
     
              }
     
     
          }
     
     
     
      }
           

BaseModel和BaseView接口里面是空的,在这里我只是为了在BasePresenter中提供一个约束。当然,如果你有其它全局的需求,可以在里面添加一些方法。重点是BasePresenter这个抽象类,传入一个View和Model,并将其用protected关键字修饰,这样,在它的子类中就可以直接对其赋值和使用了。加入CompositeSubscription变量,是为了对RxJava进行管理。unSubscribe方法对View进行null赋值和清除Rx的Subscription(订阅),防止内存泄漏。

Presnter桥梁

接下来看看这个很重要的类,作为连接Model和View的桥梁,这里又是怎么做的呢?

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
     
     
      10
     
     
      11
     
     
      12
     
     
      13
     
     
      14
     
     
      15
     
     
      16
     
     
      17
     
     
      18
     
     
      19
     
     
      20
     
     
      21
     
     
      22
     
     
      23
     
     
      24
     
     
      25
     
     
      26
     
     
      27
     
     
      28
     
     
      29
     
     
      30
     
     
      31
     
     
      32
     
     
      33
     
     
      34
     
     
      35
     
     
      36
     
     
      37
     
     
      38
     
     
      39
           
public 
      class MainPresenter extends MainContract.Presenter {
     
     
         
      public MainPresenter(MainContract.View view) {
     
     
              mView = view;
     
     
              mModel = 
      new MainModel();
     
     
          }
     
     
         
      @Override
     
         
      public void getGank() {
     
     
     
              Subscription subscribe = mModel.getGank()
     
     
                      .subscribe(
      new Subscriber<Gank>() {
     
     
                         
      @Override
     
                         
      public void onStart() {
     
     
                              mView.showDialog();
     
     
                          }
     
     
                         
      @Override
     
                         
      public void onCompleted() {
     
     
                              mView.hideDialog();
     
     
                          }
     
     
                         
      @Override
     
                         
      public void onError(Throwable e) {
     
     
                              mView.onFail(e.getMessage());
     
     
                              onCompleted();
     
     
                          }
     
     
                         
      @Override
     
                         
      public void onNext(Gank gank) {
     
     
                              mView.onSucceed(gank);
     
     
                          }
     
     
                      });
     
     
     
     
              addSubscribe(subscribe);
     
     
          }
     
     
      }
           

构造方法传进一个View,并且new了一个Model对象,直接赋值给父类中的View和Model。然后下面复写的方法中调用Model中的方法,再将结果通过View中的方法传出去,这是很原始的MVP方式。最后addSubscribe添加到订阅队列中。

Model处理数据

Model分出来,而不在Presenter处理,其实也是为了简洁,当你要处理很多数据的时候,Presenter就会变得很乱了。

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
           
public 
      class MainModel implements MainContract.Model {
     
     
         
      @Override
     
         
      public Observable<Gank> getGank() {
     
             
      return ApiEngine.getInstance().getApiService()
     
     
                      .getGank(
      "1")
     
     
                      .compose(RxSchedulers.<Gank>switchThread());
     
     
          }
     
     
      }
           

我这里很简单,就获取ApiService对象,然后调用API。最后compose传进我自己定义的线程切换器:

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
     
     
      10
     
     
      11
     
     
      12
     
     
      13
     
     
      14
           
public 
      class RxSchedulers {
     
     
         
      public 
      static <T> Observable.
      Transformer<T, T> switchThread() {
     
             
      return 
      new Observable.Transformer<T, T>() {
     
                 
      @Override
     
                 
      public Observable<T> call(Observable<T> tObservable) {
     
                     
      return tObservable
     
     
                              .subscribeOn(Schedulers.io())
     
     
                              .unsubscribeOn(Schedulers.io())
     
     
                              .observeOn(AndroidSchedulers.mainThread());
     
     
                  }
     
     
              };
     
     
          }
     
     
      }
           

将网络请求的IO线程切换回Android的UI主线程,才能继续进行在Presenter中的操作。

BaseActivity中的封装

在前面我们可以看到BasePresenter中有两个方法,一个是添加订阅addSubscribe,另一个是unSubscribe解除订阅。我们只看到了在Presenter中使用了addSubscribe,而没有看到unSubscribe在哪使用了。因为要防止内存泄漏,所以当然要在和生命周期相关的地方进行释放资源,这个地方只有我们所说的View了,也就是Activity和Fragment中。我们先开看一下相关代码:

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
     
     
      10
     
     
      11
     
     
      12
     
     
      13
     
     
      14
     
     
      15
     
     
      16
     
     
      17
     
     
      18
     
     
      19
     
     
      20
     
     
      21
     
     
      22
           
public 
      abstract 
      class BaseActivity<P extends BasePresenter> extends AppCompatActivity {
     
     
         
      protected P mPresenter;
     
     
         
      @Override
     
         
      protected void onCreate(Bundle savedInstanceState) {
     
             
      super.onCreate(savedInstanceState);
     
             
      if (onCreatePresenter() != 
      null) {
     
     
                  mPresenter = onCreatePresenter();
     
     
              }
     
     
          }
     
     
         
      @Override
     
         
      protected void onDestroy() {
     
             
      super.onDestroy();
     
             
      if (mPresenter!=
      null){
     
     
                  mPresenter.unSubscribe();
     
     
              }
     
     
          }
     
     
         
      protected abstract P onCreatePresenter();
     
     
      }
           

通过泛型确定一个Presenter的类型,然后使用抽象方法onCreatePresenter对其进行赋值,最后在onDestroy方法中进行资源的释放。继承这个BaseActivity类的Activity,就不用每次都在onDestroy进行同样的操作啦~达到简洁的目的。同理,Fragment中也是同样的,只是在生命周期的onResume和onPause中分别进行Presenter的赋值和资源的释放。这里我就不贴代码,可以上我的Github看。

Retrofit引擎封装

说到这了,就顺便说一下Retrofit吧。上面的MainModel类中用到了ApiEngine类:

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
     
     
      10
     
     
      11
     
     
      12
     
     
      13
     
     
      14
     
     
      15
     
     
      16
     
     
      17
     
     
      18
     
     
      19
     
     
      20
     
     
      21
     
     
      22
     
     
      23
     
     
      24
     
     
      25
     
     
      26
     
     
      27
     
     
      28
     
     
      29
     
     
      30
     
     
      31
     
     
      32
     
     
      33
     
     
      34
     
     
      35
     
     
      36
     
     
      37
     
     
      38
     
     
      39
     
     
      40
     
     
      41
     
     
      42
     
     
      43
     
     
      44
     
     
      45
     
     
      46
     
     
      47
     
     
      48
     
     
      49
           
public 
      class ApiEngine {
     
     
         
      private 
      volatile 
      static ApiEngine apiEngine;
     
         
      private Retrofit retrofit;
     
     
         
      private ApiEngine() {
     
     
             
      //日志拦截器
     
     
              HttpLoggingInterceptor loggingInterceptor = 
      new HttpLoggingInterceptor();
     
     
              loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
     
     
             
      //缓存
     
             
      int size = 
      1024 * 
      1024 * 
      100;
     
     
              File cacheFile = 
      new File(App.getContext().getCacheDir(), 
      "OkHttpCache");
     
     
              Cache cache = 
      new Cache(cacheFile, size);
     
     
     
              OkHttpClient client = 
      new OkHttpClient.Builder()
     
     
                      .connectTimeout(
      12, TimeUnit.SECONDS)
     
     
                      .writeTimeout(
      12, TimeUnit.SECONDS)
     
     
                      .writeTimeout(
      12, TimeUnit.SECONDS)
     
     
                      .addNetworkInterceptor(
      new NetworkInterceptor())
     
     
                      .addInterceptor(loggingInterceptor)
     
     
                      .cache(cache)
     
     
                      .build();
     
     
     
              retrofit = 
      new Retrofit.Builder()
     
     
                      .baseUrl(ApiService.BASE_URL)
     
     
                      .client(client)
     
     
                      .addConverterFactory(GsonConverterFactory.create())
     
     
                      .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
     
     
                      .build();
     
     
     
          }
     
     
         
      public static ApiEngine getInstance() {
     
             
      if (apiEngine == 
      null) {
     
                 
      synchronized (ApiEngine.class) {
     
                     
      if (apiEngine == 
      null) {
     
     
                          apiEngine = 
      new ApiEngine();
     
     
                      }
     
     
                  }
     
     
              }
     
             
      return apiEngine;
     
     
          }
     
     
         
      public ApiService getApiService() {
     
             
      return retrofit.create(ApiService.class);
     
     
          }
     
     
      }
           

用了单例模式,在构造方法中只初始化一次Retrofit和Okhttp。双重锁的方式获取单例,然后再根据需要获取ApiService,如果你有很多个不同源的API,那就可以创建多个getXXXXApiService。

结语

最后,来看看Demo运行效果吧:

优雅的使用MVP+RxJava+Retrofit框架

Demo数据源来自Gank.io

其实还有一个问题,就是关于错误类型的统一处理。刚开始的时候我是打算添加进去的,但是后来想想,每个项目的API都有不同的需求,有不同的错误,所以,还是留给需要的时候再封装一层错误处理吧。如果要统一处理错误,自定义一个Subscriber,在里面的onError方法处理即可。