一、概述
Retrofit是一个网络加载框架,底层是基于OkHttp封装的,api接口定义和api接口使用解耦(参考3),并可以通过注解构建不同的请求方式和参数,还可以支持数据解析转换(gson)以及线程调度异步请求(RxJava)的优点。
二、简单示例
这个示例是作为下面原理分析的示例,比较简单的GET请求。如果要具体了解详细的使用,可以参考鸿洋_的文章学习参考2。示例中的api使用的是鸿洋_的玩安卓网站的开放api。
- 添加权限和依赖
###AndroidManifest.xml###
<uses-permission android:name="android.permission.INTERNET" />
###build.gradle###
// implementation 'com.squareup.retrofit2:retrofit:2.5.0'
// implementation 'com.squareup.retrofit2:converter-gson:2.5.0' // 用Gson解析json
implementation 'com.squareup.retrofit2:retrofit:2.0.2'
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
- 创建一个api接口文件,可通过注解的方式构建不同的请求
public interface ApiService {
@GET("hotkey/json")
Call<HotkeyList> getHotkey();//获取热搜词
}
- 创建各请求返回结果对应的数据类. 搜索热词Hotkey数据类
public class HotkeyList {
/*
{"data":[{"id":6,"link":"","name":"面试","order":1,"visible":1},
{"id":9,"link":"","name":"Studio3","order":1,"visible":1},
{"id":5,"link":"","name":"动画","order":2,"visible":1},
{"id":1,"link":"","name":"自定义View","order":3,"visible":1},
{"id":2,"link":"","name":"性能优化 速度","order":4,"visible":1},
{"id":3,"link":"","name":"gradle","order":5,"visible":1},
{"id":4,"link":"","name":"Camera 相机","order":6,"visible":1},
{"id":7,"link":"","name":"代码混淆 安全","order":7,"visible":1},
{"id":8,"link":"","name":"逆向 加固","order":8,"visible":1}],
"errorCode":0,
"errorMsg":""}
*/
private int errorCode;
private String errorMsg;
private List<DataBean> data;
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public List<DataBean> getData() {
return data;
}
public void setData(List<DataBean> data) {
this.data = data;
}
@Override
public String toString() {
return "BannerList{" +
"errorCode=" + errorCode +
", errorMsg='" + errorMsg + '\'' +
", data=" + data +
'}';
}
public static class DataBean {
/*{"id":6,"link":"","name":"面试","order":1,"visible":1}*/
private int id;
private String link;
private String name;
private int order;
private int isVisible;
@Override
public String toString() {
return "DataBean{" +
"link='" + link + '\'' +
", id=" + id +
", imagePath='" + name + '\'' +
", isVisible=" + isVisible +
", order=" + order+"}";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public int getIsVisible() {
return isVisible;
}
public void setIsVisible(int isVisible) {
this.isVisible = isVisible;
}
}
}
- 最重要的一步,构建Retrofit去请求网络数据。
//1、创建retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://www.wanandroid.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
//2、用retrofit构建出对应的接口实例对象,里面用了代理模式
ApiService apiService = retrofit.create(ApiService.class);
//3、获取适配转换Call对象,其实返回的为是Call的衍生类OkHttpCall
Call<BannerList> call = apiService.getBanner();
//4、调用call.enqueue方法获取数据,最终调用的是okhttp的enqueue方法
call.enqueue(new Callback<BannerList>() {
@Override
public void onResponse(Call<BannerList> call, Response<BannerList> response) {
Log.d("response", response.body().toString());
}
@Override
public void onFailure(Call<BannerList> call, Throwable t) {
Log.d("onFailure", t.toString());
}
});
三、原理浅析
上面的示例中,Retrofit核心步骤分为四步:
不过在分析之前先说明2个比较重要的类,后面会用到
serviceMethod:里面通过解析注解,请求参数等等,并通过toRequest提供request给OkHtppCall以以便请求网络
OkHtppCall:底层是OkHttp实现,OkHtppCall通过serviceMethod提供的request获取请求后,将结果通过toResponse传给serviceMethod做数据转换(如gson).
- build:创建retrofit,能配置url,rxjava,gson等。
通过构建器模式,在build方法中,通过创建了OkHttpClient对象,以及转换器集合和调用适配器集合等生成Retrofit对象。
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);
}
- create:用retrofit加工出对应的接口实例对象,一个代理对象。
通过代理模式的动态代理,在create中,根据传入的接口对象,动态创建接口代理实例对象。
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
//动态代理,可拦截service内的方法method
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);
}
});
}
- call:调用对象(接口)中的方法,获取适配转换Call(/ExecutorCallbackCall/HttpCall)对象。
当调用其请求方法(getHotkey)时,会被代理拦截,并将方法method转为serviceMethod,并传递给新创建的OkHttpCall对象,并通过ServiceMethod中的adapt返回Call(ExecutorCallAdapterFactory,DefaultCallAdapterFactory)
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
......
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
......
}
那么ServiceMethod是干嘛的?serviceMethod,里面通过解析注解,请求参数等等通过toRequest提供request给OkHtppCall以以便请求网络。具体我们先看下ServiceMethod的构造函数,对其有个大概印象。
在ServiceMethod的构造参中,可以发现包含,如baseUrl,httpMethod,hasBody ,headers,isFormEncoded 等请求相关的信息。
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;
}
上面拦截代码中,首先loadServiceMethod方法中,通过构建模式,创建了一个ServiceMethod对象。并存入缓存serviceMethodCache中,防止多次创建
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;
}
在build方法中,创建了一个调用适配器callAdapter对象,并且包含解析注解、判断请求方式、请求参数解析等等操作。此外如果hasBody==false并且isMultipart或者isFormEncoded为true,那么将会抛出methodError的错误,这也验证了之前的结论:如果定义的java接口参数为空,那么@FormUrlEncoded以及@Multipart需要去掉。最后build()方法会返回一个ServiceMethod。
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);
}
if (relativeUrl == null && !gotUrl) {
throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError("Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError("Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError("Multipart method must contain at least one @Part.");
}
return new ServiceMethod<>(this);
}
再看看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);
}
}
上面的代码主要是将获取的methode的类型和注解,传入Retrofit的callAdapter(returnType, annotations)中,实际调用的是nextCallAdapter方法,nextCallAdapter通过传入的参数循环从adapterFactories集合里面取出第一个不为null的CallAdapter,CallAdapter可以通过DefaultCallAdapterFactory和ExecutorCallAdapterFactory工厂模式生成。Retrofit初始化build时默认创建了一个ExecutorCallAdapterFactory。
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;
}
}
.......
}
loadServiceMethod分析完了,那么接下来return serviceMethod.callAdapter.adapt(okHttpCall)返回的为什么是okHttpCall/ExecutorCallbackCall呢?由上分析serviceMethod创建的callAdapter其实是ExecutorCallAdapterFactory工厂类返回的Call的衍射类ExecutorCallbackCall。DefaultCallAdapterFactory中adapt返回的是参数自身,即OkhttpCall。
@Override public <R> Call<R> adapt(Call<R> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
- enqueue:调用okhttp中的enquenue方法,异步请求,onResponse和onFailure处于ui线程
通过ExecutorCallbackCall调用的enquenue,最终调用的还是传入的OKhttpCall的对象delegate的enqueue方法。
delegate.enqueue()
所以不管是通过ExecutorCallbackCall还是okHttpCall,最终调用的都是okHttpCall中的enqueue方法
@Override public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("callback == null");
okhttp3.Call call;
......
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
......
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
......
}
上面代码主要可分三部分说明。第一部分,生成一个带请求信息Request的Okhttp的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;
}
在createRawCall,调用serviceMethod中的toRequest方法去生成请求信息。
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;
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
第二部分,调用okhttp的enqueue请求
call.enqueue(new okhttp3.Callback() {......}
第三部分,解析转换返回的数据
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
在parseResponse中,将结果解析,并且调用serviceMethod.toResponse去交给转换器转换为对应的数据类。
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
T body = serviceMethod.toResponse(catchingBody);
}
####ServiceMethod####
/** Builds a method return value from an HTTP response body. */
T toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
到此,整个Retrofit就分析完了,Retrofit不仅使用注解的方式,让网络请求更简单方便,而且因为其使用了大量的设计模式去封装,所以解耦也是非常棒的。
四、总结
整个流程,简单复习下:
-
build:创建retrofit,能配置url,rxjava, gson变换器等。
-
create: 用retrofit加工出对应的接口实例对象,一个代理对象。
-
调用对象(接口)中的方法,获取适配转换Call(ExecutorCallbackCall/OkhttpCall)对象。
当调用接口中的方法时,会被代理对象拦截,调用到代理中的方法解析invoke,并将方法method转为serviceMethod,并传递给新创建的OkHttpCall对象,并通过adapt(ExecutorCallAdapterFactory,DefaultCallAdapterFactory)返回OkHttpCall]或衍生类(ExecutorCallbackCall,OkhttpCall).
serviceMethod:里面通过解析注解,请求参数等等通过toRequest提供request给OkHtppCall以以便请求网络
OkHtppCall:底层是OkHttp实现,OkHtppCall通过serviceMethod提供的request获取请求后,将结果通过toResponse传给serviceMethod做数据转换(如gson).
- call.enqueue,调用okhttp中的enquenue方法,异步请求,同serviceMethod提供的toResponse解析数据。onResponse和onFailure处于ui线程。
PS:上面是更具retrofit:2.0.2源码做的分析。retrofit:2.5.0 的话中,serviceMethod被中的变量和方法,被httpserviceMethod,RequestFactory等用设计模式再次封装了,解耦更好
参考:
1.网络加载框架 - Retrofit
2.Retrofit2 完全解析 探索与okhttp之间的关系
3.Retrofit分析-漂亮的解耦套路