美文网首页Android DemoAndroid项目框架Mvp+dagger+rxjava+retrfit+okhttp
从零开始搭建一个主流项目框架(三)—RxJava2.0+Retr

从零开始搭建一个主流项目框架(三)—RxJava2.0+Retr

作者: 海晨忆 | 来源:发表于2018-02-23 17:12 被阅读378次

    个人博客:haichenyi.com。感谢关注

      上一篇,我们把mvp+dagger加进去了,这一篇,我们把网络请求加上

      我这里的网络请求是用的装饰者模式去写的,什么是装饰者模式呢?在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。我的理解就是一个接口,两个实现类,一个实现类负责调用接口的方法,另一个类负责功能的具体实现。本文中所提到的代码都是伪代码,最后会给出完整的,最初版本的项目框架。不包含任何业务逻辑

    项目结构.png

      容我一个一个来说,首先,我们一般请求网络的时候,会有统一的返回数据格式,一个是需要判断返回code码的,就比方说登录功能,那登录成功,还是失败,我们只用判断code码即可,这种类型,我们统一是HttpNoResult。还有一个是返回数据的,就比方说查一个列表数据。这里我们统一的是HttpResult。我先给出这两个类的代码:

    package com.haichenyi.myproject.model.http;
    
    /**
     * Author: 海晨忆
     * Date: 2018/2/23
     * Desc:没有解析数据的返回
     */
    public class HttpNoResult {
      private int code;
      private String msg;
    
      public int getCode() {
        return code;
      }
    
      public HttpNoResult setCode(int code) {
        this.code = code;
        return this;
      }
    
      public String getMsg() {
        return msg;
      }
    
      public HttpNoResult setMsg(String msg) {
        this.msg = msg;
        return this;
      }
    
      @Override
      public String toString() {
        return "HttpNoResult{" + "code=" + code + ", msg='" + msg + '\'' + '}';
      }
    }
    
    package com.haichenyi.myproject.model.http;
    
    import com.google.gson.annotations.SerializedName;
    
    /**
     * Author: 海晨忆
     * Date: 2018/2/23
     * Desc:有解析数据的返回
     */
    public class HttpResult<T> {
      private int code;
      private String msg;
      @SerializedName(value = "result")
      private T data;
    
      public int getCode() {
        return code;
      }
    
      public HttpResult setCode(int code) {
        this.code = code;
        return this;
      }
    
      public String getMsg() {
        return msg;
      }
    
      public HttpResult setMsg(String msg) {
        this.msg = msg;
        return this;
      }
    
      public T getData() {
        return data;
      }
    
      public HttpResult setData(T data) {
        this.data = data;
        return this;
      }
    
      @Override
      public String toString() {
        return "HttpResult{" + "code=" + code + ", msg='" + msg + '\'' + ", data=" + data + '}';
      }
    }
    

      这里我就需要说一点,有数据返回的时候,每个数据类型都是不一样的,所以,这里我用的泛型传递,不同的数据类型,传不同的bean对象

      言归正传,我们来说说网络请求的一个接口,两个实现类。

    一个接口—HttpHelper

    package com.haichenyi.myproject.model.http;
    
    import io.reactivex.Flowable;
    
    /**
     * Author: 海晨忆
     * Date: 2018/2/23
     * Desc:网络接口,接口参数Token统一处理,方法中不传Token
     */
    public interface HttpHelper {
      /**
       * 登录时获取验证码.
       *
       * @param phone 手机号
       * @return {"code":0}
       */
      Flowable<HttpNoResult> loginCode(String phone);
      /*Flowable<HttpResult<Login>> login(String phone, String code);
      Flowable<HttpResult<List<DiyBean>>> diyKeys(String allId);*/
    }
    

      Flowable是RxJava2.0新增的,所以说RxJava完美兼容Retrofit,泛型就是我们需要解析的数据

    1. loginCode方法是说返回数据,我们只用判断是否是成功还是失败,

    2. login方法是说返回数据是一个Login对象,至于对象是什么内容,那就是和你们后台确认了

    3. diyKeys方法就是说,返回数据是一个list对象,每个list的item是DiyBean对象

    package com.haichenyi.myproject.model;
    
    import com.haichenyi.myproject.model.http.HttpHelper;
    import com.haichenyi.myproject.model.http.HttpNoResult;
    
    import io.reactivex.Flowable;
    
    /**
     * Author: 海晨忆
     * Date: 2018/2/23
     * Desc:网络请求的实现类
     */
    public class DataHelper implements HttpHelper {
      private HttpHelper http;
    
      public DataHelper(HttpHelper http) {
        this.http = http;
      }
    
      @Override
      public Flowable<HttpNoResult> loginCode(String phone) {
        return http.loginCode(phone);
      }
    }
    

      DataHelper是HttpHelper的实现类,他的唯一作用就是调用接口的方法即可,具体的功能实现是后面一个类,这里需要说明的是这个类的构造方法要public表示,因为他要dagger生成,用private或者protected表示无法生成。

    package com.haichenyi.myproject.model.http;
    
    import com.haichenyi.myproject.model.http.api.HttpApi;
    
    import io.reactivex.Flowable;
    
    /**
     * Author: 海晨忆
     * Date: 2018/2/23
     * Desc: 网络接口Retrofit实现
     */
    public class RetrofitHelper implements HttpHelper{
      private HttpApi httpApi;
    
    @Inject
      RetrofitHelper(HttpApi httpApi) {
        this.httpApi = httpApi;
      }
    
      @Override
      public Flowable<HttpNoResult> loginCode(String phone) {
        return httpApi.loginCode(phone);
      }
    }
    

      RetrofitHelper类作为HttpHelper接口的实现类,他是具体功能的实现类,为什么说他是具体功能的实现类呢?因为,他是调用HttpApi接口的方法。HttpApi接口是干什么用的呢?

    package com.haichenyi.myproject.model.http.api;
    
    import com.haichenyi.myproject.model.http.HttpNoResult;
    import com.haichenyi.myproject.model.http.ProtocolHttp;
    
    import io.reactivex.Flowable;
    import retrofit2.http.Field;
    import retrofit2.http.FormUrlEncoded;
    import retrofit2.http.POST;
    
    /**
     * Author: 海晨忆
     * Date: 2018/2/23
     * Desc:网络请求接口api
     */
    public interface HttpApi {
      /**
       * 登录时获取验证码.
       *
       * @param phone 手机号
       * @return {"code":0}
       */
      @FormUrlEncoded
      @POST(ProtocolHttp.METHOD_LOGIN_CODE)
      Flowable<HttpNoResult> loginCode(@Field("phone") String phone);
    }
    

    这个就是Retrofit的网络请求的方式,看不懂?这个就是Retrofit的东西了
    方法注解,包含@GET、@POST、@PUT、@DELETE、@PATH、@HEAD、@OPTIONS、@HTTP。
    标记注解,包含@FormUrlEncoded、@Multipart、@Streaming。
    参数注解,包含@Query、@QueryMap、@Body、@Field,@FieldMap、@Part,@PartMap。
    其他注解,包含@Path、@Header、@Headers、@Url。

    这里我们还差一个接口

    package com.haichenyi.myproject.model.http;
    
    /**
     * Author: 海晨忆
     * Date: 2018/2/23
     * Desc:
     */
    public interface ProtocolHttp {
      String HTTP_HOST = "http://xxx.xx.xxx.xxx:8080/app/con/";
      String HTTP_COMMON = "common/";
      String METHOD_LOGIN_CODE = HTTP_COMMON + "code";//登录发送验证码
    }
    

      如上,这里需要注意的是不能以""结尾,然后就是,跟你们后台商量,格式不要错了,尽量就只有接口名字不同,接口名字前面部分都是一样的。

      到此,这里基本上就说完了,那么有同鞋就会问了,接口定义方法的时候,我们知道该如何写返回数据类型呢?这个我就不知道了,你得问你们后台,根据后台返回的数据类型去写对应的bean类。推荐一个功能PostMan。

      到目前为止,我们都还没有初始化网络请求的参数,这些网络请求的参数在哪里初始化呢?这些参数,我们就只用初始化一次,我们就想到了dagger的全局单例模式,没错,就是这个,我们上一篇写了很多没有用的东西,里面有一个HttpModule

    package com.haichenyi.myproject.di.module;
    
    import com.haichenyi.myproject.di.qualifier.ApiUrl;
    import com.haichenyi.myproject.model.http.ProtocolHttp;
    import com.haichenyi.myproject.model.http.api.HttpApi;
    
    import java.util.concurrent.TimeUnit;
    
    import javax.inject.Singleton;
    
    import dagger.Module;
    import dagger.Provides;
    import okhttp3.OkHttpClient;
    import retrofit2.Retrofit;
    import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
    import retrofit2.converter.gson.GsonConverterFactory;
    
    /**
     * Author: 海晨忆
     * Date: 2018/2/23
     * Desc:网络请求的参数初始化
     */
    @Module
    public class HttpModule {
      @Provides
      @Singleton
      OkHttpClient.Builder providesOkHttpHelper() {
    //请求读写超时时间
        return new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(10, TimeUnit.SECONDS);
      }
    
      @Provides
      @Singleton
      OkHttpClient provideClient(OkHttpClient.Builder builder) {
        return builder
    //        .addInterceptor(new MyHttpInterceptor())
            .build();
      }
      
      @Provides
      @Singleton
      Retrofit.Builder providesRetrofitBuilder() {
        return new Retrofit.Builder();
      }
    
      @Provides
      @Singleton
      HttpApi provideApi(@ApiUrl Retrofit retrofit) {
        return retrofit.create(HttpApi.class);
      }
    
      @Provides
      @Singleton
      @ApiUrl
      Retrofit providesApiRetrofit(Retrofit.Builder builder, OkHttpClient client) {
        return createRetrofit(builder, client, ProtocolHttp.HTTP_HOST);//这里就是你的网络请求的url
      }
    
      private Retrofit createRetrofit(Retrofit.Builder builder, OkHttpClient client, String host) {
        return builder.client(client)
            .baseUrl(host)
            .addConverterFactory(GsonConverterFactory.create())//添加gson自动解析,我们不用关
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build();
      }
    }
    

    如上代码,注释写的都有,考过去用就行了

    在AppModule里面添加如下代码

    package com.haichenyi.myproject.di.module;
    
    import com.haichenyi.myproject.base.MyApplication;
    import com.haichenyi.myproject.model.DataHelper;
    import com.haichenyi.myproject.model.http.HttpHelper;
    import com.haichenyi.myproject.model.http.RetrofitHelper;
    
    import javax.inject.Singleton;
    
    import dagger.Module;
    import dagger.Provides;
    
    /**
     * Author: 海晨忆
     * Date: 2018/2/23
     * Desc:
     */
    @Module
    public class AppModule {
      private MyApplication application;
    
      public AppModule(MyApplication application) {
        this.application = application;
      }
    
      @Provides
      @Singleton
      DataHelper provideDataHelper(HttpHelper httpHelper) {
        return new DataHelper(httpHelper);
      }
    
      @Provides
      @Singleton
      HttpHelper provideHttpHelper(RetrofitHelper retrofitHelper) {
        return retrofitHelper;
      }
    }
    

    这里都是dagger了生成全局单例对象需要的东西

    在AppComponent里面添加如下代码

    package com.haichenyi.myproject.di.component;
    
    import com.haichenyi.myproject.di.module.AppModule;
    import com.haichenyi.myproject.di.module.HttpModule;
    import com.haichenyi.myproject.model.DataHelper;
    
    import javax.inject.Singleton;
    
    import dagger.Component;
    
    /**
     * Author: 海晨忆
     * Date: 2018/2/23
     * Desc:
     */
    @Singleton
    @Component(modules = {AppModule.class, HttpModule.class})
    public interface AppComponent {
      DataHelper getDataHelper();
    }
    

    在BaseMvpPresenter里面添加如下代码

    package com.haichenyi.myproject.base;
    
    import io.reactivex.disposables.CompositeDisposable;
    import io.reactivex.disposables.Disposable;
    
    /**
     * Author: 海晨忆
     * Date: 2018/2/23
     * Desc:
     */
    public class BaseMvpPresenter<T extends BaseView> implements BasePresenter<T> {
      protected T baseView;
      private CompositeDisposable disposables;
    
      @Override
      public void attachView(T baseView) {
        this.baseView = baseView;
      }
    
      protected void addSubscribe(Disposable disposable) {
        if (null == disposables) {
          disposables = new CompositeDisposable();
        }
        disposables.add(disposable);
      }
    
      @Override
      public void detachView() {
        this.baseView = null;
        unSubscribe();
      }
    
      private void unSubscribe() {
        if (null != disposables) {
          disposables.clear();
          disposables = null;
        }
      }
    }
    

    至此,就全部写完了,关于网络请求的内容。调用方式如下:

    package com.haichenyi.myproject.presenter;
    
    import com.haichenyi.myproject.base.BaseMvpPresenter;
    import com.haichenyi.myproject.base.MyApplication;
    import com.haichenyi.myproject.contract.MainContract;
    import com.haichenyi.myproject.model.DataHelper;
    
    import javax.inject.Inject;
    
    import io.reactivex.android.schedulers.AndroidSchedulers;
    import io.reactivex.schedulers.Schedulers;
    
    /**
     * Author: 海晨忆
     * Date: 2018/2/23
     * Desc:
     */
    public class MainPresenter extends BaseMvpPresenter<MainContract.IView>
        implements MainContract.Presenter {
      private DataHelper dataHelper;
      @Inject
      MainPresenter() {
        dataHelper = MyApplication.getAppComponent().getDataHelper();
      }
    
      @Override
      public void loadData() {
        addSubscribe(dataHelper.loginCode("134xxxxxxxx")
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe());
    //    baseView.showTipMsg("加载数据");
      }
    }
    

    记得在清单文件里面,加上网络权限

    <uses-permission android:name="android.permission.INTERNET"/>
    

    网络请求,这样调用之后在哪处理呢?我给出我的几个处理的工具类。首先,按如下图设置1.8支持lambda表达式

    配置.png

    然后添加如下几个类

    HttpCode

    package com.haichenyi.myproject.model.http;
    
    /**
     * Author: 海晨忆.
     * Date: 2017/12/21
     * Desc: 网络请求状态码
     */
    public interface HttpCode {
      /**
       * 成功.
       */
      int SUCCESS = 0;
      /**
       * 参数为空.
       */
      int NO_PARAMETER = 1;
      /**
       * 服务器错误.
       */
      int SERVER_ERR = 3;
    }
    

    ApiException

    package com.haichenyi.myproject.model.http;
    
    /**
     * Author: 海晨忆.
     * Date: 2017/12/21
     * Desc: 接口异常判断处理
     */
    public class ApiException extends Exception {
      private int code;
    
      @SuppressWarnings("unused")
      public ApiException(int code) {
        this.code = code;
      }
    
      public ApiException(int code, String message) {
        super(message);
        this.code = code;
      }
    
      public int getCode() {
        return code;
      }
    
      public ApiException setCode(int code) {
        this.code = code;
        return this;
      }
    }
    

    MyRxUtils

    package com.haichenyi.myproject.model.http;
    
    import io.reactivex.BackpressureStrategy;
    import io.reactivex.Flowable;
    import io.reactivex.FlowableTransformer;
    import io.reactivex.Scheduler;
    import io.reactivex.android.schedulers.AndroidSchedulers;
    import io.reactivex.schedulers.Schedulers;
    
    /**
     * Author: 海晨忆.
     * Date: 2017/12/27
     * Desc:切换线程的工具类
     */
    public class MyRxUtils {
      /**
       * 从其他线程转到主线程.
       *
       * @param scheduler Schedulers.io()等等
       * @param <T>       t
       * @return FlowableTransformer
       */
      public static <T> FlowableTransformer<T, T> toMain(Scheduler scheduler) {
        return upstream -> upstream.subscribeOn(scheduler).observeOn(AndroidSchedulers.mainThread());
      }
    
      public static <T> FlowableTransformer<HttpResult<T>, T> handResult() {
        return upstream -> upstream.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .flatMap(tHttpResult -> {
              if (tHttpResult.getCode() == HttpCode.SUCCESS) {
                return /*createData(tHttpResult.data)*/Flowable.just(tHttpResult.getData());
              } else {
                return Flowable.error(new ApiException(tHttpResult.getCode(), tHttpResult.getMsg()));
              }
            });
      }
    
      private static <T> Flowable<T> createData(final T data) {
        return Flowable.create(e -> {
          e.onNext(data);
          e.onComplete();
        }, BackpressureStrategy.ERROR);
      }
    }
    

    MySubscriber

    package com.haichenyi.myproject.model.http;
    
    
    import com.haichenyi.myproject.base.BaseView;
    import io.reactivex.subscribers.ResourceSubscriber;
    
    /**
     * Author: 海晨忆.
     * Date: 2017/12/21
     * Desc:
     */
    public abstract class MySubscriber<T> extends ResourceSubscriber<T> {
      private BaseView baseView;
      private boolean showLoading;
    
      public MySubscriber(BaseView baseView) {
        this.baseView = baseView;
      }
    
      public MySubscriber(BaseView baseView, boolean showLoading) {
        this.baseView = baseView;
        this.showLoading = showLoading;
      }
    
      @Override
      protected void onStart() {
        super.onStart();
        if (null != baseView && showLoading) {
          baseView.showLoading();
        }
      }
    
      @Override
      public void onError(Throwable t) {
        if (null == baseView) {
          return;
        }
        baseView.hideLoading();
        if (t instanceof ApiException) {
          ApiException apiException = (ApiException) t;
          switch (apiException.getCode()) {
            case HttpCode.NO_PARAMETER:
              baseView.showTipMsg("参数为空");
              break;
            case HttpCode.SERVER_ERR:
              baseView.showTipMsg("服务器错误");
              break;
            default:
              break;
          }
        }
      }
    
      @Override
      public void onComplete() {
        if (null != baseView) {
          baseView.hideLoading();
        }
      }
    }
    

    这几个类不想多做解释,结合注释,仔细看几遍,就知道是干嘛用的了

    加上这几个之后调用方式就变成了以下的方式:

    package com.haichenyi.myproject.presenter;
    
    import com.haichenyi.myproject.base.BaseMvpPresenter;
    import com.haichenyi.myproject.base.MyApplication;
    import com.haichenyi.myproject.contract.MainContract;
    import com.haichenyi.myproject.model.DataHelper;
    import com.haichenyi.myproject.model.http.HttpNoResult;
    import com.haichenyi.myproject.model.http.MyRxUtils;
    import com.haichenyi.myproject.model.http.MySubscriber;
    
    import javax.inject.Inject;
    
    import io.reactivex.schedulers.Schedulers;
    
    /**
     * Author: 海晨忆
     * Date: 2018/2/23
     * Desc:
     */
    public class MainPresenter extends BaseMvpPresenter<MainContract.IView>
        implements MainContract.Presenter {
      private DataHelper dataHelper;
    
      @Inject
      MainPresenter() {
        dataHelper = MyApplication.getAppComponent().getDataHelper();
      }
    
      @Override
      public void loadData() {
        addSubscribe(dataHelper.loginCode("134xxxxxxxx")
            .compose(MyRxUtils.toMain(Schedulers.io()))
            .subscribeWith(new MySubscriber<HttpNoResult>(baseView, true) {
              @Override
              public void onNext(HttpNoResult httpNoResult) {
    
              }
            }));
    //    baseView.showTipMsg("加载数据");
      }
    }
    

    完了,完了,终于写完了。

    项目链接

    相关文章

      网友评论

      • andriod小学徒:demo的使用不能夠再多写点使用的案例吗?还花了5个积分下载的
        海晨忆:我记得应该是有一个例子的呀,一个知道怎么用不就可以了吗?
      • 悟丶饭:最近一个项目的框架就是这种类型的,感觉了解很多了,谢谢楼主
        海晨忆:@15050690552 面向接口编程,通过子类Contract的子view,那个接口,定义方法,在P层中通过BaseView,调用这个方法即可。现在推荐使用LiveData,传递数据,这个系列的专题里面,我新写的有对应的文章。
        f8e98e7837a9:得到数据后 更新UI 还要再传入V层的实例吗 没有例子啊
        海晨忆:@悟丶饭 不客气,能帮助到你就行

      本文标题:从零开始搭建一个主流项目框架(三)—RxJava2.0+Retr

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