Retrofit是Square公司出品的基于OkHttp封装的一套RESTful(目前流行的一套api设计的风格)网络请求框架。它内部使用了大量的设计模式,以达到高度解耦的目的;它可以直接通过注解的方式配置请求;可以使用不同的Http客户端;还可以使用json Converter序列化数据,直接转换成你期望生成的实体bean;它还支持Rxjava等等等
封装和思考
- RxJava如何与Retrofit结合
- 相同格式的Http请求数据该如何封装
- 相同格式的Http请求数据统一进行预处理
- 如何取消一个Http请求 -- 观察者之间的对决,Oberver VS Subscriber
- 一个需要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": {}
}
我们想要对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
*
* @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
本文介绍了Retrofit网络请求框架,它基于OkHttp封装,使用大量设计模式。阐述了RxJava与Retrofit结合的用法,还探讨了相同格式Http请求数据的封装和预处理,通过创建HttpResult类包装结果,利用Observable的map方法处理泛型和错误,让开发者只需关注Data数据类型。
3921

被折叠的 条评论
为什么被折叠?



