Retrofit出来也蛮久了,每次听其他小伙伴说起来都是那种吊吊的感觉,所以自己也赶紧加入其中,用完之后感觉真的很棒,当然学习的时候也是遇到不少问题,爽歪歪的感脚。具体该怎么用Retrofit,推荐鸿洋的文章 Retrofit2 完全解析 探索与okhttp之间的关系 ,写的比较清楚,我也不多花笔墨在这上面了,下面要说的是我个人使用Retrofit遇到的一些问题。
首先是引用库了,在module的build.gradle文件中添加
compile ‘io.reactivex:rxjava:1.1.6‘
compile ‘io.reactivex:rxandroid:1.2.1‘
compile ‘com.squareup.retrofit2:retrofit:2.1.0‘
compile ‘com.squareup.retrofit2:converter-gson:2.0.2‘
compile ‘com.squareup.retrofit2:adapter-rxjava:2.0.1‘
前两个是RxJava需要用到的库,中间的是Retrofit2的库,最后两个是Retrofit增加对Gson转换以及Rxjava支持的库,还有一个拦截器的
compile ‘com.squareup.okhttp3:logging-interceptor:3.0.1‘
可以方便的打印出网络请求的各种请求头,返回数据等,十分方便,不过在编译的时候我遇到了问题,所以我直接拷贝了源码HttpLoggingInterceptor这个类的代码,修改了一下拿来使用。在这个拦截器中,我们可以为请求添加统一的header
@Override
public Response intercept(Chain chain) throws IOException {
Level level = this.level;
Request request = chain.request();
request = request.newBuilder().addHeader("header", "value").build();
if (level == Level.NONE) {
return chain.proceed(request);
}
...
}
然后为OkhttpClient配置这个拦截器
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.retryOnConnectionFailure(true)
.connectTimeout(15, TimeUnit.SECONDS)
.build();
我们在使用Retrofit进行post请求的时候可以这么写
public interface UserService {
@POST("getuser")
Observable<BaseResult<User>> getUser(@Body RequestBody request);
}
比较尴尬的是我这样写提交到服务端的时候,服务端老是获取不到我提交的数据,后来沟通交流之后发现,服务端从Header和Form中查找需要的数据,而我提交到服务器并不在这两部分里面
User user = new User();
user.userCode = "0000000";
MediaType JSON = MediaType.parse("application/json");
RequestBody body = RequestBody.create(JSON ,new Gson().toJson(user));
retrofit.create(UserService.class).getUser(body);
我是这么提交的,然而并没有什么用,后来查看RequestBody的继承关系之后看到了FormBody之后,原来还有这么一个直接提交Form键值对的类,真是粗心大条啊,使用这个类构建body,ok,木有问题了,服务端妥妥的拿到了数据,我也能愉快的玩耍下去了。
对于上面post请求的写法,有时候仅仅需要一两个参数我们就没必要那么麻烦了,写法可以如下:
@POST("getuser")
@FormUrlEncoded
Observable<BaseResult<User>> getUser(@Field("userCode") String userCode);
需要注意的是记得加上@FormUrlEncoded,不然运行时会报错。在retrifit构造这个接口的实例时,首先会根据接口方法中的注解来创建不同的ServiceMethod,ServiceMethod再解析参数注解来构建不同的ParameterHandler,各种各样的ParameterHandler如图:
之后Retrofit根据ServiceMethod实例构建出了一个OkhttpCall,OkhttpCall在发起请求的时候再调用ServiceMethod的toRequest()方法,构建出一个Request,之后就是okhttp的请求流程,在toRequest()方法中,ServiceMethod根据之前构建的ParameterHandler去apply()运行每一组注解值,在这个过程里我们看到到了Field在apply中其实就是往RequestBuilder请求构造器中的FormBody添加了表单值,同理的其他的ParameterHandler在apply的过程中也是根据各自的属性去构造完善这个Request,最后交给okhttp调用。
接下来是对服务端返回的数据进行统一的预处理,通常服务端会返回一个这样的结果
{
"success":true,
"errorCode":"OK"
"data":{...}
}
data可以是数组,也可以是object,看具体情况,这样子看来我们可以封装一下:
class BaseResult<T>{
boolean success;
String errorCode;
T data;
}
嗯嗯,这样看起来还不错呢,然后结合上我们的Rxjava,来看看我们现在的代码:
Retrofit retrofit = new Retrofit.Builder().build();//实例化随便写写,大家就略过吧
UserService userService = retrofit.create(UserService.class);
FormBody.Builder builder = new FormBody.Builder();
builder.add("userCode","00000");
FormBody body = builder.build();
Observable<BaseResult<User>> user = userService.getUser(body);
user.subscribe(new Observer<BaseResult<User>>() {
@Override
public void onNext(BaseResult<User> userBaseResult) {
if(userBaseResult.success){
//...
}else{
//on error
}
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
//on error
}
});
等等,看起来有点怪怪的,每次我们都需要判断一下请求的结果是否成功,这样是不是比较low,一次请求就多了一次判断,感觉身心都很累啊,,于是乎我找了找资源,有两篇文章有提到了对服务器结果进行统一的预处理,给出传送门:
最后我是这么封装的
public class BaseFunc<T> implements Func1<BaseResult<T>,Observable<T>>{
@Override
public Observable<T> call(final BaseResult<T> tBaseResult) {
if(tBaseResult.success){
return Observable.create(new Observable.OnSubscribe<T>() {
@Override
public void call(Subscriber<? super T> subscriber) {
try {
subscriber.onNext(tBaseResult.data);
subscriber.onCompleted();
}catch (Exception e){
subscriber.onError(e);
}
}
});
}else{
return Observable.error(new ApiException(tBaseResult.errorCode));
}
}
}
具体调用方式:
Retrofit retrofit = new Retrofit.Builder().build();//实例化随便写写,大家就略过吧
UserService userService = retrofit.create(UserService.class);
FormBody.Builder builder = new FormBody.Builder();
builder.add("userCode","00000");
FormBody body = builder.build();
Observable<BaseResult<User>> user = userService.getUser(body);
user.flatMap(new BaseFunc<User>()).subscribe(new Observer<User>() {
@Override
public void onNext(User user) {
}
@Override
public void onError(Throwable e) {
//on error
}
@Override
public void onCompleted() {
}
});
其实就是在返回的结果上面再加一个Rx操作符观察BaseResult的具体值,最后留给我们的就是我们只想要的结果。
好了,具体想说的就是这么多,为什么会写这篇文章主要是FormBody挖的坑(其实是自己对http了解的不够),还有就是对其他作者提出的封装方法进行实践,结果失败了。。。老尴尬,最后自己封装了一下(为啥失败原因还在找。。。)以后有想到了再来补充。有什么错误的话,也请指出,Thank you.
补充:刚才去看了一下Rx处理服务器请求、缓存的完美封装的demo,作者使用的compose操作符对BaseResult进行的转换的问题是没有问题的,他的写法是 --》 类名.<具体泛型类>handleResult(),而我一直是没有加上具体泛型类(从来没有遇到这样的的一个泛型方法,果然是我太年轻,
/(ㄒoㄒ)/~~),此外如果没有使用类名去调用的话,依然编译不通过,这因该是编译器把<>当成了大于小于符号了,由此导致的。
原文地址:http://linwg.blog.51cto.com/9120692/1838425