Android Rx家族之RxJava2.0与 Retrofit2结合的封装和项目中的使用

Retrofit是Square公司出品的基于OkHttp封装的一套RESTful(目前流行的一套api设计的风格)网络请求框架。它内部使用了大量的设计模式,以达到高度解耦的目的;它可以直接通过注解的方式配置请求;可以使用不同的Http客户端;还可以使用json Converter序列化数据,直接转换成你期望生成的实体bean;它还支持Rxjava等等等

 

封装和思考

  1. RxJava如何与Retrofit结合
  2. 相同格式的Http请求数据该如何封装
  3. 相同格式的Http请求数据统一进行预处理
  4. 如何取消一个Http请求 -- 观察者之间的对决,Oberver VS Subscriber
  5. 一个需要ProgressDialog的Subscriber该有的样子



写法《一》:单纯使用Retrofit,不加Rxjava的使用

/**
* 描述:第一步:定义一个接口配置网络请求
*/
public interface WeatherService {
//  网络接口的使用为查询天气的接口
//  
   @GET("weather_mini")
//  此处回调返回的可为任意类型Call<T>,再也不用自己去解析json数据啦!!!
   Call<WeatherEntity> getMessage(@Query("city") String city);

 

/**
    * 单纯使用Retrofit的联网请求
    */
   private void doRequestByRetrofit() {
       Retrofit retrofit = new Retrofit.Builder()
               .baseUrl(API.BASE_URL)//基础URL 建议以 / 结尾
               .addConverterFactory(GsonConverterFactory.create())//设置 Json 转换器
               .build();
       WeatherService weatherService = retrofit.create(WeatherService .class);
       Call<WeatherEntity> call = weatherService.getMessage("北京");
       call.enqueue(new Callback<WeatherEntity>() {
           @Override
           public void onResponse(Call<WeatherEntity> call, Response<WeatherEntity> response) {
               //测试数据返回
               WeatherEntity weatherEntity = response.body();
               Log.e("TAG", "response == " +  weatherEntity.getData().getGanmao());
           }

           @Override
           public void onFailure(Call<WeatherEntity> call, Throwable t) {
               Log.e("TAG", "Throwable : " + t);
           }
       });
   }


写法《二》 Retrofit + Rxjava
区别:使用Rxjava后,返回的不是Call<T>而是一个Observable<T>的对象了。

 

public interface RxWeatherService {
   @GET("weather_mini")
   Observable<WeatherEntity> getMessage(@Query("city") String city);
}

 

 private void doRequestByRxRetrofit() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(API.BASE_URL)//基础URL 建议以 / 结尾
                .addConverterFactory(GsonConverterFactory.create())//设置 Json 转换器
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//RxJava 适配器
                .build();
        RxWeatherService rxjavaService = retrofit.create(RxWeatherService .class);
        rxjavaService .getMessage("北京")
                .subscribeOn(Schedulers.io())//IO线程加载数据
                .observeOn(AndroidSchedulers.mainThread())//主线程显示数据
                .subscribe(new Subscriber<WeatherEntity>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(WeatherEntity weatherEntity) {
                Log.e("TAG", "response == " + weatherEntity.getData().getGanmao());
                    }
                });
    }

 

案例二:

创建Retrofit

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(baseUrl)
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build();

这样一来我们定义的service返回值就不在是一个Call了,而是一个Observable

public interface MovieService {
    @GET("top250")
    Observable<MovieEntity> getTopMovie(@Query("start") int start, @Query("count") int count);
}

getMovie方法为:

//进行网络请求
private void getMovie(){
    String baseUrl = "https://api.douban.com/v2/movie/";

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build();

    MovieService movieService = retrofit.create(MovieService.class);

    movieService.getTopMovie(0, 10)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<MovieEntity>() {
                @Override
                public void onCompleted() {
                    Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onError(Throwable e) {
                    resultTV.setText(e.getMessage());
                }

                @Override
                public void onNext(MovieEntity movieEntity) {
                    resultTV.setText(movieEntity.toString());
                }
            });
}

接下来我们把创建Retrofit的过程封装一下,然后希望Activity创建Subscriber对象传进来。

二次封装:

将请求过程进行封装

public class HttpMethods {

    public static final String BASE_URL = "https://api.douban.com/v2/movie/";

    private static final int DEFAULT_TIMEOUT = 5;

    private Retrofit retrofit;
    private MovieService movieService;

    //构造方法私有
    private HttpMethods() {
        //手动创建一个OkHttpClient并设置超时时间
        OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
        httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);

        retrofit = new Retrofit.Builder()
                .client(httpClientBuilder.build())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .build();

        movieService = retrofit.create(MovieService.class);
    }

    //在访问HttpMethods时创建单例
    private static class SingletonHolder{
        private static final HttpMethods INSTANCE = new HttpMethods();
    }

    //获取单例
    public static HttpMethods getInstance(){
        return SingletonHolder.INSTANCE;
    }

    /**
     * 用于获取豆瓣电影Top250的数据
     * @param subscriber 由调用者传过来的观察者对象
     * @param start 起始位置
     * @param count 获取长度
     */
    public void getTopMovie(Subscriber<MovieEntity> subscriber, int start, int count){
        movieService.getTopMovie(start, count)
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(subscriber);
    }
}

用一个单例来封装该对象,在构造方法中创建Retrofit和对应的Service。 如果需要访问不同的基地址,那么你可能需要创建多个Retrofit对象,或者干脆根据不同的基地址封装不同的HttpMethod类。

getMovie()   其中subscriber是MainActivity的成员变量。

private void getMovie(){
    subscriber = new Subscriber<MovieEntity>() {
        @Override
        public void onCompleted() {
            Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onError(Throwable e) {
            resultTV.setText(e.getMessage());
        }

        @Override
        public void onNext(MovieEntity movieEntity) {
            resultTV.setText(movieEntity.toString());
        }
    };
    HttpMethods.getInstance().getTopMovie(subscriber, 0, 10);
}

相同格式的Http请求数据该如何封装

{
    "resultCode": 0,
        "resultMessage": "成功",
        "data": {}
}

大部分的Http服务可能都是这样设置,resultCode和resultMessage的内容相对比较稳定,而data的内容变化多端,72变都不一定够变的,有可能是个User对象,也有可能是个订单对象,还有可能是个订单列表。 按照我们之前的用法,使用Gson转型需要我们在创建subscriber对象是指定返回值类型,如果我们对不同的返回值进行封装的话,那可能就要有上百个Entity了,看着明明是很清晰的结构,却因为data的不确定性无奈了起来。

我们可以创建一个HttpResult类

public class HttpResult<T> {
    private int resultCode;
    private String resultMessage;

    private T data;
}

如果data是一个User对象的话。那么在定义Service方法的返回值就可以写为

Observable<HttpResult<User>>

 

这样一来HttpResult就相当于一个包装类,将结果包装了起来,但是在使用的时候要给出一个明确的类型。

在上面的示例中,我也创建了一个HttpResult类,用来模仿这个形式,将其中的Subject单独封装了起来。

public class HttpResult<T> {

    //用来模仿resultCode和resultMessage
    private int count;
    private int start;
    private int total;
    private String title;

    //用来模仿Data
    private T subjects;
}

这样泛型的时候就要写为:

Observable<HttpResult<List<Subject>>>

相同格式的Http请求数据统一进行预处理

既然我们有了相同的返回格式,那么我们可能就需要在获得数据之后进行一个统一的预处理。

当接收到了一个Http请求结果之后,由于返回的结构统一为

{
    "resultCode": 0,
        "resultMessage": "成功",
        "data": {}
}

我们想要对resultCoderesultMessage先做一个判断,因为如果resultCode == 0代表success,那么resultCode != 0data一般都是null

Activity或Fragment对resultCoderesultMessage基本没有兴趣,他们只对请求状态data数据感兴趣。

基于这种考虑,我们在resultCode != 0的时候,抛出个自定义的ApiException。这样就会进入到subscriber的onError中,我们可以在onError中处理错误信息。

另外,请求成功时,需要将data数据转换为目标数据类型传递给subscriber,因为,Activity和Fragment只想拿到和他们真正相关的数据。

使用Observable的map方法可以完成这一功能。

HttpMethods中创建一个内部类HttpResultFunc,代码如下

/**
 * 用来统一处理Http的resultCode,并将HttpResult的Data部分剥离出来返回给subscriber
 *
 * @param <T> Subscriber真正需要的数据类型,也就是Data部分的数据类型
 */
private class HttpResultFunc<T> implements Func1<HttpResult<T>, T>{

    @Override
    public T call(HttpResult<T> httpResult) {
        if (httpResult.getResultCode() != 0) {
            throw new ApiException(httpResult.getResultCode());
        }
        return httpResult.getData();
    }
}

然后我们的getTopMovie方法改为:

public void getTopMovie(Subscriber<List<Subject>> subscriber, int start, int count){

    movieService.getTopMovie(start, count)
            .map(new HttpResultFunc<List<Subject>>())
            .subscribeOn(Schedulers.io())
            .unsubscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(subscriber);
}

由于HttpResult中的泛型T就是我们希望传递给subscriber的数据类型,而数据可以通过httpResult的getData方法获得,这样我们就处理了泛型问题,错误处理问题,还有将请求数据部分剥离出来给subscriber

这样我们只需要关注Data数据的类型,而不必在关心整个过程了。

需要注意一点,就是在定义Service的时候,泛型是

   HttpResult<User>
//or
    HttpResult<List<Subject>>

而在定义Subscriber的时候泛型是 java User //or List<Subject>

不然你会得到一个转型错误。

 

 

demo地址:

https://github.com/GraceJoJo/Designer

 

 

 

 

参考博客:

https://www.jianshu.com/p/6922337b4f88

http://gank.io/post/56e80c2c677659311bed9841

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值