Retrofit是Square公司出品的基于OkHttp封装的一套RESTful(目前流行的一套api设计的风格)网络请求框架。它内部使用了大量的设计模式,以达到高度解耦的目的;它可以直接通过注解的方式配置请求;可以使用不同的Http客户端;还可以使用json Converter序列化数据,直接转换成你期望生成的实体bean;它还支持Rxjava等等等
封装和思考
写法《一》:单纯使用Retrofit,不加Rxjava的使用
/**
* 描述:第一步:定义一个接口配置网络请求
*/
public interface WeatherService {
// 网络接口的使用为查询天气的接口
//
@GET("weather_mini")
// 此处回调返回的可为任意类型Call,再也不用自己去解析json数据啦!!!
Call 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 call = weatherService.getMessage("北京");
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
//测试数据返回
WeatherEntity weatherEntity = response.body();
Log.e("TAG", "response == " + weatherEntity.getData().getGanmao());
}
@Override
public void onFailure(Call call, Throwable t) {
Log.e("TAG", "Throwable : " + t);
}
});
}
写法《二》 Retrofit + Rxjava
区别:使用Rxjava后,返回的不是Call
public interface RxWeatherService {
@GET("weather_mini")
Observable 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() {
@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") ObservablegetTopMovie(@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() { @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(Subscribersubscriber, 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() { @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); }
{ "resultCode": 0, "resultMessage": "成功", "data": {} }
大部分的Http服务可能都是这样设置,resultCode和resultMessage的内容相对比较稳定,而data的内容变化多端,72变都不一定够变的,有可能是个User对象,也有可能是个订单对象,还有可能是个订单列表。 按照我们之前的用法,使用Gson转型需要我们在创建subscriber对象是指定返回值类型,如果我们对不同的返回值进行封装的话,那可能就要有上百个Entity了,看着明明是很清晰的结构,却因为data的不确定性无奈了起来。
我们可以创建一个HttpResult类
public class HttpResult{ private int resultCode; private String resultMessage; private T data; }
如果data是一个User对象的话。那么在定义Service方法的返回值就可以写为
Observable>
这样一来HttpResult就相当于一个包装类,将结果包装了起来,但是在使用的时候要给出一个明确的类型。
在上面的示例中,我也创建了一个HttpResult类,用来模仿这个形式,将其中的Subject单独封装了起来。
public class HttpResult{ //用来模仿resultCode和resultMessage private int count; private int start; private int total; private String title; //用来模仿Data private T subjects; }
这样泛型的时候就要写为:
Observable>>
既然我们有了相同的返回格式,那么我们可能就需要在获得数据之后进行一个统一的预处理。
当接收到了一个Http请求结果之后,由于返回的结构统一为
{ "resultCode": 0, "resultMessage": "成功", "data": {} }
我们想要对resultCode和resultMessage先做一个判断,因为如果resultCode == 0
代表success,那么resultCode != 0
时data一般都是null。
Activity或Fragment对resultCode和resultMessage基本没有兴趣,他们只对请求状态和data数据感兴趣。
基于这种考虑,我们在resultCode != 0
的时候,抛出个自定义的ApiException。这样就会进入到subscriber的onError中,我们可以在onError中处理错误信息。
另外,请求成功时,需要将data数据转换为目标数据类型传递给subscriber,因为,Activity和Fragment只想拿到和他们真正相关的数据。
使用Observable的map方法可以完成这一功能。
在HttpMethods中创建一个内部类HttpResultFunc,代码如下
/** * 用来统一处理Http的resultCode,并将HttpResult的Data部分剥离出来返回给subscriber * * @paramSubscriber真正需要的数据类型,也就是Data部分的数据类型 */ private class HttpResultFunc implements Func1 , T>{ @Override public T call(HttpResult httpResult) { if (httpResult.getResultCode() != 0) { throw new ApiException(httpResult.getResultCode()); } return httpResult.getData(); } }
然后我们的getTopMovie方法改为:
public void getTopMovie(Subscriber> subscriber, int start, int count){ movieService.getTopMovie(start, count) .map(new HttpResultFunc
>()) .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber); }
由于HttpResult中的泛型T就是我们希望传递给subscriber的数据类型,而数据可以通过httpResult的getData方法获得,这样我们就处理了泛型问题,错误处理问题,还有将请求数据部分剥离出来给subscriber
这样我们只需要关注Data数据的类型,而不必在关心整个过程了。
需要注意一点,就是在定义Service的时候,泛型是
HttpResult//or HttpResult >
而在定义Subscriber的时候泛型是 java User //or List
不然你会得到一个转型错误。
demo地址:
https://github.com/GraceJoJo/Designer
参考博客:
https://www.jianshu.com/p/6922337b4f88
http://gank.io/post/56e80c2c677659311bed9841