MVP+RxJava+Retrofit学习

664 阅读8分钟

一:添加依赖

1. 在build.gradle中添加rxjava、retrofit等依赖

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
compile 'com.jakewharton:butterknife:7.0.0'

/** rxjava **/
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'

/** retrofit **/
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'

/**  okhttp  **/
compile 'com.squareup.okhttp3:okhttp:3.3.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
复制代码

}

2. 加入 OkHttp 配置
         // 创建 OKHttpClient
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(1000*60, TimeUnit.SECONDS);//连接超时时间
        builder.writeTimeout(1000*60,TimeUnit.SECONDS);//写操作 超时时间
        builder.readTimeout(1000*60,TimeUnit.SECONDS);//读操作超时时间
复制代码
复制代码
3. 加入 retrofit 配置
        ApiConfig.BASE_URL = "https://wanandroid.com/";
        // 创建 OKHttpClient
        OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
        httpBuilder.connectTimeout(1000*60, TimeUnit.SECONDS);//连接超时时间
        httpBuilder.writeTimeout(1000*60,TimeUnit.SECONDS);//写操作 超时时间
        httpBuilder.readTimeout(1000*60,TimeUnit.SECONDS);//读操作超时时间
        
        // 创建Retrofit
       mRetrofit = new Retrofit.Builder() 
       .client(httpBuilder.build()) 
       .addConverterFactory(GsonConverterFactory.create()) 
       .baseUrl(ApiConfig.BASE_URL) 
       .build();
复制代码
复制代码
public class CommentResponse {

    private Integer errorCode;

    private String errorMsg;

    private CommentBean date;

}

public class CommentBean {
    private int total;

    private int size;

    private List<CommentData> datas;

     class CommentData{

         private int articleId;
         private boolean canEdit;
         private String content;
         private int id;
         private String niceDate;
         private Date publishDate;
         private int rootCommentId;
         private int status;
         private int toUserId;
         private String toUserName;
         private int userId;
         private String userName;
         private int zan;
    }
}

复制代码

注意:rxjava|rxandroid版本需要一致、retrofit|rxjava版本一致;如果不一致会报错。

二:新建mvp中的v

我们知道从视图view的角度上看一次完整的请求过程包括:显示加载框–>加载数据成功(加载失败)–>更新UI(提示用户)–>关闭正在加载的框。

而这几个步骤我们可以认为大部分请求都是这样的,那么我们就可以将他封装成一个基类视图IBaseView

  • 定义基类视图IBaseView

    package bule.souv.com.mvp_rxjava_retrofittest_master.base;
    
    /**
    * 描述:视图基类
    * 作者:dc on 2017/2/16 10:59
    * 邮箱:597210600@qq.com
    */
    public interface IBaseView<T> {
    /**
    * @descriptoin  请求前加载progress
    * @author   dc
    * @date 2017/2/16 11:00
    */
    void showProgress();
    
    /**
    * @descriptoin  请求结束之后隐藏progress
    * @author   dc
    * @date 2017/2/16 11:01
    */
    void disimissProgress();
    
    /**
    * @descriptoin  请求数据成功
    * @author   dc
    * @param tData 数据类型
    * @date 2017/2/16 11:01
    */
    void loadDataSuccess(T tData);
    
    /**
    * @descriptoin  请求数据错误
    * @author   dc
    * @param throwable 异常类型
    * @date 2017/2/16 11:01
    */
    void loadDataError(Throwable throwable);
    }
    复制代码
    

将这几个请求步骤也成公共方法,然后子类继承这个基类就能获取到这几个公共方法,避免了每个请求的子view都重复定义

  • 定义WeatherView子类视图

    
    
      /**
    * 描述:评论信息的mvp中的视图V
    */
      public interface CommentView extends IBaseView<CommentResponse> {
      }
    复制代码
    

三:新建MVP中的model

M在MVP中主要处理业务数据逻辑;如:网络请求数据、数据库获取数据等等。

我们在此就就是通过网络数据评论数据

  • CommentModel的接口定义

**

 

    /**
    * 描述:MVP中的M;处理获取网络评论数据
 
    */
    public interface CommentModel<T> {

    /**
    * @descriptoin  获取网络数据
     */
    void loadComment(String questionId, IBaseRequestCallBack<T> iBaseRequestCallBack);

    }
复制代码

主要提供两个方法,一个通过questionId来获取数据,IBaseRequestCallBack为数据回调接口,这个在下面会讲到。

  • 定义数据的回调接口IBaseRequestCallBack

**

    package bule.souv.com.mvp_rxjava_retrofittest_master.base;

/**
 * 描述:请求数据的回调接口
 * Presenter用于接受model获取(加载)数据后的回调
 * 作者:dc on 2017/2/16 11:22
 * 邮箱:597210600@qq.com
 */
public interface IBaseRequestCallBack<T> {

/**
 * @descriptoin 请求之前的操作
 * @author  dc
 * @date 2017/2/16 11:34
 */
void beforeRequest();

/**
 * @descriptoin 请求异常
 * @author  dc
 * @param throwable 异常类型
 * @date 2017/2/16 11:34
 */
void requestError(Throwable throwable);

/**
 * @descriptoin 请求完成
 * @author  dc
 * @date 2017/2/16 11:35
 */
void requestComplete();

/**
 * @descriptoin 请求成功
 * @author  dc
 * @param callBack 根据业务返回相应的数据
 * @date 2017/2/16 11:35
 */
void requestSuccess(T callBack);
}
复制代码

请求之前的操作一样、用Throwable来处理请求异常信息、请求完成之后的操作一样、只有请求成功返回的Object不一样,在这里使用T泛型来指定不同的object对象。

  • 定义retrofit中service的Api

**

 
    
    /**
     * 描述:retrofit的接口service定义
   
     */
    public interface CommentServiceApi {

    
    @GET("wenda/comments/{questionId}/json?")
    Observable<CommentInfoBean> loadCommentsInfo(@Query("questionId") String questionId);

    }
复制代码

这里首先需要注意请求的接口地址使用retrofit不要拼接错了。提供了两个参数;

  • 定义实现CommentModel的CommentImp

    **

     
      public class CommentImp extends BaseModel implements CommentModel<CommentInfoBean> {
    
      private Context context = null;
      private CommentServiceApi commentServiceApi;
      private CompositeSubscription mCompositeSubscription;
    
      public CommentImp(Context mContext) {
          super();
          context = mContext;
          commentServiceApi = retrofitManager.getService();
          mCompositeSubscription = new CompositeSubscription();
      }
    
      @Override
      public void loadComment(String questionId, final IBaseRequestCallBack iBaseRequestCallBack) {
          mCompositeSubscription.add(mCompositeSubscription.loadCommentsInfo(key, city)  //将subscribe添加到subscription,用于注销subscribe
                .observeOn(AndroidSchedulers.mainThread())//指定事件消费线程
                .subscribeOn(Schedulers.io())  //指定 subscribe() 发生在 IO 线程
                .subscribe(new Subscriber<WeatherInfoBean>() {
    
                    @Override
                    public void onStart() {
                        super.onStart();
                        //onStart它总是在 subscribe 所发生的线程被调用 ,如果你的subscribe不是主线程,则会出错,则需要指定线程;
                        iBaseRequestCallBack.beforeRequest();
                    }
    
                    @Override
                    public void onCompleted() {
                        //回调接口:请求已完成,可以隐藏progress
                        iBaseRequestCallBack.requestComplete();
                    }
    
                    @Override
                    public void onError(Throwable e) {
                        //回调接口:请求异常
                        iBaseRequestCallBack.requestError(e);
                    }
    
                    @Override
                    public void onNext(WeatherInfoBean weatherInfoBean) {
                        //回调接口:请求成功,获取实体类对象
                        iBaseRequestCallBack.requestSuccess(weatherInfoBean);
                    }
                }));
      }
    
      @Override
       
      }
    复制代码
    

因为实现了接口CommentModel所以也实现了接口的两个方法。

新建MVP中的P

P在MVP中主要是视图V和模式M之间的桥梁。

  • 定义CommentPresenter接口

    **

      package bule.souv.com.mvp_rxjava_retrofittest_master.presenter;
    
          /**
           * 描述:MVP中的P接口定义
         
           */
          public interface CommentPresenter {
    
          /**
           * @descriptoin 请求评论数据
           * @author  dc
       
           * @return
           */
          void loadWeather(String questionId);
      
        
      }
    复制代码
    
  • 定义presenter的基类

上面我们的IBaseCallBack,我们定义了几个回调方法,方法里面都是处理请求之前、之后的结果,这里也可以新建个Presenter的基类用来建立不同的M和V之间的桥梁(因为是公共的方法)只是M和V不同而已。也因为不同的M和V对象,同样我们也用泛型来代替

**

    package bule.souv.com.mvp_rxjava_retrofittest_master.base;

    /**
     * 描述:
     *  * 代理对象的基础实现 :  一些基础的方法
     *
     * @param <V> 视图接口对象(view) 具体业务各自继承自IBaseView
     * @param <T> 业务请求返回的具体对象
     * 作者:dc on 2017/2/16 15:07
     * 邮箱:597210600@qq.com
     */
    public class BasePresenterImp<V extends IBaseView , T> implements IBaseRequestCallBack<T> {

    private IBaseView iBaseView = null;  //基类视图

    /**
     * @descriptoin  构造方法
     * @author  dc
     * @param view 具体业务的视图接口对象
     * @date 2017/2/16 15:12
     */
    public BasePresenterImp(V view) {
        this.iBaseView = view;
    }

    /**
     * @descriptoin 请求之前显示progress
     * @author  dc
     * @date 2017/2/16 15:13
     */
    @Override
    public void beforeRequest() {
        iBaseView.showProgress();
    }

    /**
     * @descriptoin 请求异常显示异常信息
     * @author  dc
     * @param throwable 异常信息
     * @date 2017/2/16 15:13
     */
    @Override
    public void requestError(Throwable throwable) {
        iBaseView.loadDataError(throwable);
        iBaseView.disimissProgress(); //请求错误,提示错误信息之后隐藏progress
    }

    /**
     * @descriptoin 请求完成之后隐藏progress
     * @author  dc
     * @date 2017/2/16 15:14
     */
    @Override
    public void requestComplete() {
        iBaseView.disimissProgress();
    }

    /**
     * @descriptoin 请求成功获取成功之后的数据信息
     * @author  dc
     * @param callBack 回调的数据
     * @date 2017/2/16 15:14
     */
    @Override
    public void requestSuccess(T callBack) {
        iBaseView.loadDataSuccess(callBack);
    }

}
复制代码

在代码中我们可以看到,加载前的弹框,加载成功回调给UI,加载失败通知UI错误信息,加载完成关闭弹框等都已经在这里做了一个基础的实现(这就是为什么定义一个基类的原因)。

这里我们通过两个泛型V and T,V表示不同的视图、T表示不同的对象。这样只要在不同的presenter传入不同的V和T就能通过基类来建立不同的视图V和不同的模型M之间的桥梁

  • 实现CommentPresenter接口的CommentPresenterImp

**

       
        public class CommentPresenterImp extends BasePresenterImp<CommentView,WeatherInfoBean> implements CommentPresenter { //传入泛型V和T分别为CommentView、CommentInfoBean表示建立这两者之间的桥梁
        private Context context = null;
        privateCommentModelImp commentModelImp = null;
    
        /**
         * @descriptoin 构造方法
         * @param view 具体业务的视图接口对象
         * @author dc
         * @date 2017/2/16 15:12
         */
        publicCommentPresenterImp(CommentrView view, Context context) {
            super(view);
            this.context = context;
            this.commentModelImp = new CommentrModelImp(context);
        }
    
        @Override
        public void loadComment(String questionid
  ) {
            commentModelImp.loadComment(questionid, this);
        }
    
      
    }
复制代码

在类的定义代码中我们继承了基类BasePresenterImp并传入泛型V、T分别为CommentView、CommentInfoBean则表示建立这两者之间的桥梁

也因为我们在BasePresenterImp中实现了IBaseCallBack的回调方法,所以程序Model请求之后通过回调接口调用BasePresenterImp中不同的方法,而我们在不同的方法中处理了视图V的界面,从而P就建立了M的数据处理和V视图的显示处理之间的桥梁

Activity或Fragment的实现

这里面的代码处理都很简单,因为MVP的好处讲activity的M和V分离开了的好处。

**

    package bule.souv.com.mvp_rxjava_retrofittest_master;

    import android.app.ProgressDialog;
    import android.os.Bundle;
    import android.support.design.widget.FloatingActionButton;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    
    import bule.souv.com.mvp_rxjava_retrofittest_master.bean.WeatherInfoBean;
    import bule.souv.com.mvp_rxjava_retrofittest_master.presenter.WeatherPresenterImp;
    import bule.souv.com.mvp_rxjava_retrofittest_master.view.WeatherView;
    import butterknife.Bind;
    import butterknife.ButterKnife;
    import butterknife.OnClick;
    
    public class MainActivity extends AppCompatActivity implements WeatherView{

    @Bind(R.id.toolbar)
    Toolbar toolbar;
    @Bind(R.id.main_getweather_temp_btn)
    Button mainGetweatherTempBtn;
    @Bind(R.id.main_showweather_temp_tv)
    TextView mainShowweatherTempTv;
    @Bind(R.id.fab)
    FloatingActionButton fab;

    /**  对象定义  **/
    private WeatherPresenterImp weatherPresenterImp = null;
    private ProgressDialog progressDialog = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        setSupportActionBar(toolbar);

        init();

    }

    /**
     * @descriptoin 点击获取温度
     * @author
     * @param
     * @date 2017/2/16 15:52
     * @return
     */
    @OnClick(R.id.main_getweather_temp_btn)
    public void setOnClickRequestWeatherBtn(){
        weatherPresenterImp.loadWeather("c5bb749112664353af44bc99ed263857", "长沙");
    }

    /**
     * @descriptoin 初始化
     * @author  dc
     * @date 2017/2/16 15:44
     */
    private void init(){
        weatherPresenterImp = new WeatherPresenterImp(this,this);
        progressDialog = new ProgressDialog(MainActivity.this);
        //        progressDialog.setTitle();
        progressDialog.setMessage("正在请求获取数据,请稍等!!!");
    }


    @Override
    public void showProgress() {
        if(progressDialog != null && !progressDialog.isShowing()){
            progressDialog.show();
        }
    }

    @Override
    public void disimissProgress() {
        if(progressDialog != null && progressDialog.isShowing()){
            progressDialog.dismiss();
        }
    }


    @Override
    public void loadDataSuccess(WeatherInfoBean tData) {
        String temperature = tData.getResult().getRealtime().getWeather().getTemperature();
        mainShowweatherTempTv.setVisibility(View.VISIBLE);
        mainShowweatherTempTv.setText("当前的气温为:" + temperature + "℃");
    }

    @Override
    public void loadDataError(Throwable throwable) {
        String errorMsg = throwable.getMessage();
        mainShowweatherTempTv.setVisibility(View.VISIBLE);
        mainShowweatherTempTv.setText(errorMsg);
    }

    @Override
    protected void onStop() {
        super.onStop();
        //在退出之后注销subscribe
        weatherPresenterImp.unSubscribe();
    }
}