一.基本使用
1.retrofit是什么
官网的解释是一个类型安全的HTTP请求客户端。它的底层的网络请求是基于 OkHttp 的,Retrofit 对其做了封装,提供了即方便又高效的网络访问框架,解决了线程切换,嵌套调用,数据转换的问题。
2.retrofit怎么用
我们先来看个简单get请求的例子,主要有三步:
1.通过retrofit的builder构建了一个retrofit的实列;
2.声明接口和获取接口代理,我们在ApiService中声明了一个get请求的方法,并且我们通过retrofit.create(xxx.class)创建了当前接口的代理对象;
3.调用接口的方法,我们直到在接口中定义的方法返回的是一个call对象,它是retrofit的call对象,当我们调用call.equeue对象,就会执行内部okHttp的call对象去执行网络请求,然后回调返回结果给我们。
less
OkHttpClient okHttpClient=new OkHttpClient.Builder()
//添加日志拦截器
.addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
System.out.println("HttpLoggingInterceptor message:"+message);
}
}).setLevel(HttpLoggingInterceptor.Level.BODY))
.build();
//创建retrofit实列
Retrofit retrofit=new Retrofit.Builder()
.client(okHttpClient) //设置okhttpClient
.addConverterFactory(ScalarsConverterFactory.create())//设置转换器
.baseUrl(url)
.build();
vbnet
public interface ApiService {
/**
* 最简单的GET请求,无参数
*/
@GET("get")
Call<String> getInfo1();
}
apiService = retrofit.create(ApiMethod.class);
typescript
apiService.getInfo1().enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
String body = response.body();
System.out.println(body);
}
@Override
public void onFailure(Call<String> call, Throwable t) {
}
});
3.retrofit的注解
通过上面的例子,我们已经知道了,在声明的接口中,我们都是通过注解来给请求做标记的,然后retrofit的内部就可以根据注解来解析对应的方法,那常用的注解有那些呢?
下面我们就来简单的介绍下:
1.常用请求方法注解
它是用来标识请求方法的注解。
| @GET | GET请求,作用在接口的方法上,表示它是一个GET请求 |
|---|---|
| @POST | POST请求,作用在接口的方法上,表示它是一个POST请求 |
| @HTTP | 通过注解可以替换以上所有注解,它拥有三个属性:method、path、hasBody |
| @DELETE | DELETE请求,作用在接口方法上,表示是一个DELETE请求 |
less
/**
* 最简单的GET请求,无参数
*/
@GET("get")
Call<String> getInfo1();
/* POST请求
* 其中@FormUrlEncoded的含义是发送已编码的Form表单
* 加这个注解后,请求的Content-Type会是application/x-www-form-urlencoded;
* 加这个注解后,发送的参数必须是@Field标记的表单键值对
*/
@FormUrlEncoded
@POST("post")
Call<String> getInfo2(@Field("sid") String id);
/**
* Delete请求
*/
@DELETE("api/fetch/guesslike/{id}")
Call<String> getInfo3(@Path("id") String id, @Body String requestEntity);
/**
* 下面定义了一个Delete请求,其实@HTTP标签是可以替代所有的请求标签,包括GET/POST/PUT/DELETE等,
* 都可以通过@HTTP标签来实现。hasBody表示是否有请求体,有就用参数@Body带上
*/
@HTTP(method = "DELETE", path = "api/fetch/guesslike/{id}", hasBody = true)
Call<String> getInfo4(@Path("id") String id, @Body String requestEntity);
2.请求头注解
它为我们的请求增加请求头信息。
| @Headers | 用于添加固定请求头,可以同时添加多个,通过该注解的请求头不会相互覆盖,而是共同存在 |
|---|---|
| @Header | 作为方法的参数传入,用于添加不固定的header,它会更新已有请求头 |
less
/**
* GET请求,@Headers的作用是添加单个固定的请求头
*/
@Headers("Cache-Control: max-age=640000")
@GET("api/fetch/guesslike")
Call<String> getInfo10();
/**
* GET请求,@Headers添加了多个固定的请求头
*/
@Headers({"Cache-Control: max-age=640000", "Accept-Charset: UTF-8"})
@GET("api/fetch/guesslike")
Call<String> getInfo11();
/**
* GET请求,@Header是用在参数上,添加单个不固定的请求头,值由外部传进来
*/
@GET("api/fetch/guesslike")
Call<String> getInfo12(@Header("Accept-Charset") String charset);
3.请求参数注解
| @Query | 用于Get请求中的参数 |
|---|---|
| @QueryMap | 与Query类似,用于不确定表单参数 |
| @Path | 用于Url中的占位符 |
| @Filed | 多用于Post方式传递参数,需要结合@FromUrlEncoded使用,即以表单的形式传递参数 |
| @FiledMap | 用于表单字段,默认接受类型是Map<String,RequestBody>,可用于实现多文件上传 |
| @Body | 多用于Post请求发送非表达数据,根据转换方式将实例对象转化为对应字符串传递参数,比如使用Post发送Json数据 |
| @Part | 用于表单字段,Part和PartMap与@multipart注解结合使用,适合文件上传的情况 |
| @PartMap | 用于表单字段,默认接受类型是Map<String,RequestBody>,可用于实现多文件上传 |
| @Url | 指定请求路径 |
less
/**
* GET请求,带请求参数
* 请求参数用@Query注解,其中注解里的名字表示追加到URL后的参数名字,
* URL后的参数值即为我们传进来的类型String name/String age
* 所以该请求完整的地址是host/api/fetch/guesslike?name=name&pwd=pwd
*/
@GET("get")
Call<String> getInfo2(@Query("name") List<String> stringList, @Query("pwd") String pwd);
/**
* GET请求,带请求参数
* 对getInfo2的改进,多个参数可以用@ueryMap标记,都放到一个Map里
*/
@GET("get")
Call<String> getInfo3(@QueryMap Map<String, String> paramsMap);
/**
* GET请求,@Path标记的作用就是使用传进来的值动态替换URL里的字段{id}
*/
@GET("get/fetch/{id}/guesslike")
Call<String> getInfo4(@Path("id") String id);
/**
* POST请求
* 其中@FormUrlEncoded的含义是发送已编码的Form表单
* 加这个注解后,请求的Content-Type会是application/x-www-form-urlencoded;
* 加这个注解后,发送的参数必须是@Field标记的表单键值对
*/
@FormUrlEncoded
@POST("post")
Call<String> getInfo5(@Field("sid") String id);
/**
* POST请求
* 和getInfo5没区别,@FieldMap仅仅表示多个请求参数可以放到一个Map里方便传递而已
*/
@FormUrlEncoded
@POST("post")
Call<String> getInfo6(@FieldMap Map<String, String> map);
/**
* POST请求,但是没有使用Form表单的形式,即没有@FormUrlEncoded
* 那么请求的Content-Type默认会是application/json;
* 不添加@FormUrlEncoded,那么就不能用@Field进行传递参数了,只有一种形式就是@Body,它就是用于非表单的请求体
* retrofit会把请求的实体序列化成一个json发送出去
*/
@POST("post")
Call<String> upLoadString(@Body String body);
/**
* 和upLoadString一样,只是请求参数是官方的RequestBody
* 这样做的好处是,我们自己构建的RequestBody可以修改其Media-Type
*/
@POST("post")
Call<String> upLoadRequestBody(@Body RequestBody requestBody);
/**
* POST请求
* 其中@Multipart表示请求体是分为多个部分的,配合参数@Part使用。
* 如果@Part的参数类型是RequestBody,表示请求参数,那么其Key,也就是"name"和"age"是不能隐藏的,必须显式指明Key
* 如果@Part的参数类型是MultipartBody.Part,即表示请求的上传文件,内容会直接被使用,那么是不需要Key的
*/
@Multipart
@POST("post")
Call<String> getInfo14(@Part("name") RequestBody name, @Part("age") RequestBody age,
@Part MultipartBody.Part file);
/**
* POST请求,getInfo14的改良版。多个请求参数可以封装在@PartMap标记对应的Map里
*/
@Multipart
@POST("post")
Call<String> getInfo15(@PartMap Map<String, RequestBody> paramsMap,
@Part MultipartBody.Part file);
4.请求和响应格式(标记)注解
| @FromUrlCoded | 表示请求发送编码表单数据,每个键值对需要使用@Filed注解 |
|---|---|
| @Multipart | 表示请求发送form_encoded数据(使用于有文件上传的场景),每个键值对需要用@Part来注解键名,随后的对象需要提供值 |
| @Streaming | 表示响应用字节流的形式返回,如果没有使用注解,默认会把数据全部载入到内存中,该注解在下载大文件时特别有用 |
4.retrofit中的数据解析器和call转换器
1.数据解析器
retrofit可以通过addConverterFactory设置数据解释器,例如添加Gson解释器。这是为了使来自接口的json结果会自动解析成定义好的字段和类型都相符的json对象接受类,在Retrofit 2.0中已经没有Converter,需要自己创建一个Converter, 不然Retrofit只能接收字符串结果,最后需要自己去解析。
| 数据解析器 | gradle依赖 |
|---|---|
| Gson | com.squareup.retrofit2:converter-gson:2.0.2 |
| Jackson | com.squareup.retrofit2:converter-jackson:2.0.2 |
| Simple XML | com.squareup.retrofit2:converter-simplexml:2.0.2 |
| Protobuf | com.squareup.retrofit2:converter-protobuf:2.0.2 |
| Moshi | com.squareup.retrofit2:converter-moshi:2.0.2 |
| Wire | com.squareup.retrofit2:converter-wire:2.0.2 |
| Scalars | com.squareup.retrofit2:converter-scalars:2.0.2 |
数据解析器还是蛮好理解的,它会把请求返回的结果,通过解析器解析成特定的格式,回调给用户。
2.数据转换器
retrofit同时也是支持数据转换器的将请求的retrofit的call转换成特定的对象,比如.addCallAdapterFactory(RxJava2CallAdapterFactory.create())将请求返回的call包装成Observable对象,rxjava2的gradle依赖为:
arduino
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.7.2'
那数据转换器又是在哪里操作的呢?这里简单的说下,它主要有以下几个步骤:
1.获取接口中的请求的方法的返回值类型;
2.通过转换器工厂获取转换器;
3.通过转换器的adapter方法,生成特定的转换器类型,比如如果你添加了 rxjava2,他就会帮你生成一个Observable<Resonse> 这个观察者,当你订阅的时候,call请求就会执行,并且就请求的结果,通过你注册的观察者传递给你,如果你还有下游观察者,则回调流回依次往下游传递;
二.原理介绍
其实通过前面的使用学习,我们已经大致知道了retrofit的基本原理,首先它是一个对于okHttp请求的二次封装,比如:1.我们可以通过接口和注解通过动态代理生成call;2.对于call的返回我们可以通过转换器和解析器将数据返回,并且支持rxjava和协程,协程我们后面再说,那rxjava就可以做数据切换,链式调用等一系列逻辑,完美解决了回调地狱的代码。所以下面我也就从 retrofit的构建及call的生成、转换器和解析器的工作来刨析原理。
1.retrofit的构建及注解生成的call
- retrofit的构建:
也是通过构建者模式,可以设置baseurl、okHttpClient、数据转换器和数据解析器。
- call的生成和调用
在retrofit中,当我们向调用接口中的方法时,我们先是通过retrofit.create(xxx.class)生成一个对象,并用接口去接收这个对象,那这个create方法是如何生成对象的呢?我们跟下源码:
typescript
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);//
}
});
}
很明显,这里面是一个动态代码,当我们在外部使用接口调用它的xxx方法时,其实就是调用到了loadServiceMethod(method).invoke(args);这个方法,我们来看下这个方法:
sql
//ServiceMethod缓存
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = ServiceMethod.parseAnnotations(this, method); //解析注解
serviceMethodCache.put(method, result);
}
}
return result;
}
原来它是调用ServiceMethod中的解析注解,我们继续跟源码
scss
abstract class ServiceMethod<T> {
//解析方法上的注解
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(
method,
"Method return type must not include a type variable or wildcard: %s",
returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract @Nullable T invoke(Object[] args);
}
HttpServiceMethod.java
//解析注解
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations();//获取方法上的注解
Type adapterType;
if (isKotlinSuspendFunction) {
Type[] parameterTypes = method.getGenericParameterTypes();
Type responseType =
Utils.getParameterLowerBound(
0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
// Unwrap the actual body type from Response<T>.
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
continuationWantsResponse = true;
} else {
// TODO figure out if type is nullable or not
// Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
// Find the entry for method
// Determine if return type is nullable or not
}
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
adapterType = method.getGenericReturnType();
}
//获取转换器 这里回做比对的
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();//转化器的返回类型
if (responseType == okhttp3.Response.class) {
throw methodError(
method,
"'"
+ getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
if (responseType == Response.class) {
throw methodError(method, "Response must include generic type (e.g., Response<String>)");
}
// TODO support Unit for Kotlin?
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
//获取解析器 这里会做比对的 根据type
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
/**非协程的走这里,传入请求、转换器、数据解析器、callFactory(实际上就是okHttpClient,它在reftrofit中赋值)
*请求有了,转换器、解析器、和okHttpClient都有了,就可以干活了->调用invoke()方法。
**/
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
try {
//noinspection unchecked
return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create call adapter for %s", returnType);
}
}
//invoke是调用流程
@Override
final @Nullable ReturnT invoke(Object[] args) {
/**创建了一个Call对象,是 OkHttpCall,这个不就是在 ApiGet 这个接口声明的
Call 对象吗?然后再看
*OkHttpCall 的enqueue方法,不就知道是怎么进行请求,怎么回调的了吗?
**/
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);//adapt
}
static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
private final CallAdapter<ResponseT, ReturnT> callAdapter;
CallAdapted(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter,
CallAdapter<ResponseT, ReturnT> callAdapter) {
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}
//adapt会执行到这里,通过转换器转换请求
@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
}
通过上面的流程我们可以看到,它通过注解解析后最终返回的是一个CallAdapter对象,并且这个对象持有了数据请求工厂,解析器和转换器。
2.call转换器的工作流程
我们接着上面流程继续分析,我们通过注解解析后,当我们执行invoke方法时,实际上它会执行CallAdapter中的adapt方法,那这个方法会调到我们设置的call转换器的方法中去,这里我们就以rxjava为例吧,我们跟下rxjava适配器的代码:
ini
public Object adapt(Call<R> call) {
Observable<Response<R>> responseObservable = isAsync
? new CallEnqueueObservable<>(call)//异步
: new CallExecuteObservable<>(call);
Observable<?> observable;
if (isResult) {
observable = new ResultObservable<>(responseObservable);
} else if (isBody) {
observable = new BodyObservable<>(responseObservable);
} else {
observable = responseObservable;
}
if (scheduler != null) {
observable = observable.subscribeOn(scheduler);
}
if (isFlowable) {
return observable.toFlowable(BackpressureStrategy.LATEST);
}
if (isSingle) {
return observable.singleOrError();
}
if (isMaybe) {
return observable.singleElement();
}
if (isCompletable) {
return observable.ignoreElements();
}
return RxJavaPlugins.onAssembly(observable);
}
我们跟下异步的请求,可以看到,它其实就是将我们传过来的call包裹到一个CallEnqueueObservable对象中去,我们继续跟CallEnqueueObservable源码:
java
final class CallEnqueueObservable<T> extends Observable<Response<T>> {
private final Call<T> originalCall;
CallEnqueueObservable(Call<T> originalCall) {
this.originalCall = originalCall;
}
@Override protected void subscribeActual(Observer<? super Response<T>> observer) {
// Since Call is a one-shot type, clone it for each new observer.
Call<T> call = originalCall.clone();
CallCallback<T> callback = new CallCallback<>(call, observer);//retrofit请求回调
observer.onSubscribe(callback);
if (!callback.isDisposed()) {
call.enqueue(callback);//调用retrofit的请求方法
}
}
private static final class CallCallback<T> implements Disposable, Callback<T> {
private final Call<?> call;
private final Observer<? super Response<T>> observer;
private volatile boolean disposed;
boolean terminated = false;
CallCallback(Call<?> call, Observer<? super Response<T>> observer) {
this.call = call;
this.observer = observer;
}
@Override public void onResponse(Call<T> call, Response<T> response) {
if (disposed) return;
try {
observer.onNext(response);
if (!disposed) {
terminated = true;
observer.onComplete();
}
} catch (Throwable t) {
Exceptions.throwIfFatal(t);
if (terminated) {
RxJavaPlugins.onError(t);
} else if (!disposed) {
try {
observer.onError(t);
} catch (Throwable inner) {
Exceptions.throwIfFatal(inner);
RxJavaPlugins.onError(new CompositeException(t, inner));
}
}
}
}
@Override public void onFailure(Call<T> call, Throwable t) {
if (call.isCanceled()) return;
try {
observer.onError(t);
} catch (Throwable inner) {
Exceptions.throwIfFatal(inner);
RxJavaPlugins.onError(new CompositeException(t, inner));
}
}
@Override public void dispose() {
disposed = true;
call.cancel();
}
@Override public boolean isDisposed() {
return disposed;
}
}
}
原来,它在注册这个被观察者时执行了call的请求,然会将回调回来的结果通过观察者将结果返回给了用户,至此,生成观察者,调用call请求,并将call请求的结果通过观察者返回给用户的流程就结束了,但是这里并没有说数据是如何解析的呢?我们接着往下看。
3.数据解析器的工作流程
前面我们说到,在rxadapter中,当我们订阅时它会执行retrofit的call方法的equeue执行请求,我们跟下源码:
scss
@Override
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
//执行okHttp的call请求过程
call.enqueue(
new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return;
}
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace(); // TODO this is not great
}
}
@Override
public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace(); // TODO this is not great
}
}
});
}
@Override
public synchronized boolean isExecuted() {
return executed;
}
@Override
public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = getRawCall();
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
// Remove the body's source (the only stateful object) so we can pass the response along.
rawResponse =
rawResponse
.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
if (code < 200 || code >= 300) {
try {
// Buffer the entire body to avoid future I/O.
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
T body = responseConverter.convert(catchingBody);//通过解析器解析数据
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}
原来它是通过okHttp请求后再将数据通过解析器去解析即可。
4.图解整个工作流程
