Retrofit2简单的说就是一个网络请求的适配器,它将一个基本的Java接口通过动态代理的方式翻译成一个HTTP请求,并通过OkHttp去发送请求。此外它还具有强大的可扩展性,支持各种格式转换以及RxJava。本文基于Retrofit2解析。
Retrofit2基本使用
先定义一个PersonalProtocol
的java接口
public interface PersonalProtocol {
/**
* 用户信息
* @param page
* @return
*/
@FormUrlEncoded
@POST("user/personal_list_info")
Call<Response<PersonalInfo>> getPersonalListInfo(@Field("cur_page") int page);
}
@FormUrlEncoded
注解表示Form表单,另外还有@Multipart
等注解,如果接口不需要传递参数,那么@FormUrlEncoded
以及@Multipart
需要去掉,具体原因后面章节会讲到。@POST
表示post请求,此外还可以使用@GET
请求
现在看看Retrofit
的使用
private void requestRetrofit(){
Retrofit retrofit = new Retrofit.Builder().baseUrl("www.xxxx.com/").build();
PersonalProtocol personalProtocol = retrofit.create(PersonalProtocol.class);
Call<Response<PersonalInfo>> call = personalProtocol.getPersonalListInfo(12);
call.enqueue(new Callback<Response<PersonalInfo>>() {
@Override
public void onResponse(Call<Response<PersonalInfo>> call, Response<Response<PersonalInfo>> response) {
//数据请求成功
}
@Override
public void onFailure(Call<Response<PersonalInfo>> call, Throwable t) {
//数据请求失败
}
});
}
首先将域名传入构造一个Retrofit
,然后通过retrofit
中的create
方法传入一个Java接口并得到一个PersonalProtocol
(当然PersonalProtocol
这个对象是经过处理了的,这个后面会讲到)调用getPersonalListInfo(12)
然后返回一个Call
,最后这个Call
调用了enqueue
方法去异步请求http,这就是一个基本的Retrofit
的网络请求。Retrofit2
中Call
接口的默认实现是OkHttpCall
,它默认使用OkHttp3
作为底层http
请求client
。
其实Retrofit还有很多方法,我们现在选两个一起来看看:
OkHttpClient okHttpClient = new OkHttpClient();
Retrofit retrofit = new Retrofit.Builder().baseUrl("www.xxxx.com")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(buildGson()))
.build();
addConverterFactory
方法中使用了gson
去解析json
,如果接口返回的json
中int
为异常数据,我们做了一个简单的转换,给它一个默认值为0:
public static Gson buildGson() {
Gson gson = new GsonBuilder()
.serializeNulls()
.registerTypeAdapter(int.class, new GsonIntegerDefaultAdapter())
.create();
return gson;
}
public class GsonIntegerDefaultAdapter implements JsonSerializer<Integer>, JsonDeserializer<Integer> {
@Override
public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
try {
if (json.getAsString().equals("") || json.getAsString().equals("null")) {//定义为int类型,如果后台返回""或者null,则返回0
return 0;
}
} catch (Exception ignore) {
}
try {
return json.getAsInt();
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
@Override
public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src);
}
}
此外,我们再来看看RxJava的使用:
OkHttpClient okHttpClient = new OkHttpClient();
Retrofit retrofit = new Retrofit.Builder().baseUrl("www.xxxx.com")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(buildGson()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
addCallAdapterFactory
方法中使用了RxJavaCallAdapterFactory
,网络请求也需要修改:
PersonalProtocol personalProtocol = retrofit.create(PersonalProtocol.class);
rx.Observable<PersonalInfo> observable = personalProtocol.getPersonalListInfo(12);
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())//最后在主线程中执行
.subscribe(new Subscriber<PersonalInfo>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
//请求失败
}
@Override
public void onNext(PersonalInfo personalInfo) {
//请求成功
}
});
同时PersonalProtocol
接口也需要改变:
public interface PersonalProtocol {
/**
* 用户信息
* @param page
* @return
*/
@FormUrlEncoded
@POST("user/personal_list_info")
Observable<PersonalInfo> getPersonalListInfo(@Field("cur_page") int page);
// Call<Response<PersonalInfo>> getPersonalListInfo(@Field("cur_page") int page);
}
基本的用法大概了解了一下,现在来看看具体实现
Retrofit2实现原理
Retrofit2
的build()
方法
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
文章开头的时候说过,Retrofit
会和OkHttp
一起使用,而我们创建的时候没有传递OkHttp
进去,build()
这个方法里面会创建一个OkHttpClient()
,当然你可以在外面传递进入,比如像下面代码一样:
OkHttpClient okHttpClient = new OkHttpClient();
Retrofit retrofit = new Retrofit.Builder().baseUrl("www.xxxx.com").client(okHttpClient).build();
接下来我们来看看create
方法的实现:
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, 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);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
调用create()
方法会返回一个范型T,我们这里是PersonalProtocol
对象,当PersonalProtocol
调用自身方法的时候,因为它运用了动态代理,所以会调用invoke
方法。invoke
方法内先将method
转换为ServiceMethod
对象,再将serviceMethod
和args
对象传入OkHttpCall
构造方法中,返回一个OkHttpCall
,最后将OkHttpCall
传入adapt
方法,最后返回一个Call
对象。
private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
loadServiceMethod
方法通过method去serviceMethodCache集合对象中获取缓存。那ServiceMethod
究竟是一个什么东东?先看看ServiceMethod
中方法的实现。
ServiceMethod.Builder
ServiceMethod(Builder<T> builder) {
this.callFactory = builder.retrofit.callFactory();
this.callAdapter = builder.callAdapter;
this.baseUrl = builder.retrofit.baseUrl();
this.responseConverter = builder.responseConverter;
this.httpMethod = builder.httpMethod;
this.relativeUrl = builder.relativeUrl;
this.headers = builder.headers;
this.contentType = builder.contentType;
this.hasBody = builder.hasBody;
this.isFormEncoded = builder.isFormEncoded;
this.isMultipart = builder.isMultipart;
this.parameterHandlers = builder.parameterHandlers;
}
在ServiceMethod的构造参数里面发现,包含了一些请求的信息,如baseUrl,httpMethod,hasBody ,isFormEncoded 等信息。
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError("'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
if (!hasBody) {
if (isMultipart) {
throw methodError(
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (isFormEncoded) {
throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
+ "request body (e.g., @POST).");
}
}
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
if (Utils.hasUnresolvableType(parameterType)) {
throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
parameterType);
}
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
if (parameterAnnotations == null) {
throw parameterError(p, "No Retrofit annotation found.");
}
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
//省略部分代码。。。
return new ServiceMethod<>(this);
}
build()
方法中,会创建callAdapter
,也会循环调用parseMethodAnnotation(annotation)
,此外如果hasBody==false
并且isMultipart
或者isFormEncoded
为true
,那么将会抛出methodError
的错误,这也验证了之前的结论:如果定义的java
接口参数为空,那么@FormUrlEncoded
以及@Multipart
需要去掉。最后build()
方法会返回一个ServiceMethod
。
先看看createCallAdapter
private CallAdapter<?> createCallAdapter() {
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError("Service methods cannot return void.");
}
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(e, "Unable to create call adapter for %s", returnType);
}
}
在createCallAdapter
方法中主要就是获取method
类型和注解,最后再调用callAdapter(returnTYpe,annotations)
,现在进入retrofit
的callAdapter(returnType, annotations)
方法:
public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
checkNotNull(returnType, "returnType == null");
checkNotNull(annotations, "annotations == null");
int start = adapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = adapterFactories.size(); i < count; i++) {
CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
//移除处理代码省略。。。
}
callAdapter
方法实际调用的是nextCallAdapter
,通过传入的参数去循环在adapterFactories
集合里面取出不为null
的CallAdapter
并返回。
parseMethodAnnotation(annotation):
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
if (!Void.class.equals(responseType)) {
throw methodError("HEAD method must use Void as response type.");
}
} else if (annotation instanceof PATCH) {
//省略部分代码。。。
}
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
if (this.httpMethod != null) {
throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
this.httpMethod, httpMethod);
}
this.httpMethod = httpMethod;
this.hasBody = hasBody;
// 省略部分代码。。。
this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value);
}
parseMethodAnnotation(annotation)
方法里面主要是判断注解的类型,然后分别调用方法对ServiceMethod
的成员变量进行赋值。
最后我们来看看ServiceMethod
的toRequest
方法:
/** Builds an HTTP request from method arguments. */
Request toRequest(Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
//省略部分代码。。。
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
在toRequest
方法中,将处理好的变量通过RequestBuilder
封装,最后返回一个HTTP request
。看到这里我们大概明白ServiceMethod
是拿来做什么的了。大概总结一下:ServiceMethod
接收到传入Retrofit
对象和Method
对象之后,ServiceMethod
通过获得method
里面的参数,然后调用各种解析器,最后将处理好的变量赋值给ServiceMethod
成员变量,最后在toRequest
方法中将这些参数给封装成OkHttp3
的一个Request
对象。
现在回到Retrofit的create方法中:
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
获取serviceMethod
之后,会将其作为参数继续封装为OkHttpCall
对象;createRawCall
会从serviceMethod
中拿到Request
,并转换为Call
。
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
parseResponse
方法中会处理接口返回的结果,并调用serviceMethod
中的toResponse
方法去转换接口请求回来的数据,前面的例子,我们是给Retrofit设置的gson转换,因此这里是使用gson转换成java对象。
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();
//处理返回码,代码省略。。。
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
T body = serviceMethod.toResponse(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;
}
}
T toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
通过前面的讲解,我们发现ServiceMethod
中封装了接口请求的数据,而OkHttpCall
则从ServiceMethod
中获得一个Request
对象,最后返回一个Call对象,在接口回调之后,通过ServiceMethod
中的Converter
将接口中的ResponseBody 转换成Java
对象。
拿到okHttpCall
之后,将它传入adapt
方法:我们在来看看Retrofit
的create
方法。
public <T> T create(final Class<T> service) {
//省略部分代码。。。。
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
// //省略部分代码。。。。
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
重点看方法里面的两行有return的代码,调用create方法的时候返回一个泛型T,我们这里是PersonalProtocol
,当我们调用PersonalProtocol
的getPersonalListInfo
的时候会通过动态代理,并返回serviceMethod.callAdapter.adapt(okHttpCall)
也就是一个Call对象:
@Override public <R> Call<R> adapt(Call<R> call) {
return call;
}
于是就有了之前的代码:
Call<Response<PersonalInfo>> call = personalProtocol.getPersonalListInfo(12);
call.enqueue(new Callback<Response<PersonalInfo>>() {
@Override
public void onResponse(Call<Response<PersonalInfo>> call, Response<Response<PersonalInfo>> response) {
//数据请求成功
}
@Override
public void onFailure(Call<Response<PersonalInfo>> call, Throwable t) {
//数据请求失败
}
});
好了就这样了 2021新年开工第一篇