首先我們知道retrofit底層是基于OkHttp的:
接著我們通過一個測試類,看一下retrofit的一般使用方法:
public class RetrofitTest {
interface Weather{
@GET("/v3/weather/weatherInfo")
Call get(@Query("city") String city, @Query("key") String key);
}
@Test
public void test(){
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://restapi.amap.com/").build();
Weather weather = retrofit.creat(Weather.class);
Call call = weather.get("北京", "13cb58f5884f9749287abbead9c658f2");
try {
Response response = call.execute();
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}
我們可以看出Retrofit是通過Builder模式生成的,這樣的優(yōu)點應(yīng)該都知道,不僅能幫我們初始化一系列的參數(shù),還可以讓調(diào)用者不用過于關(guān)心內(nèi)部構(gòu)造。
接著我們看Retrofit的creat傳入的是一個接口類,所以這里應(yīng)該是會用到代理模式,通過代理模式拿到Weather的方法,再通過反射的方法獲取到我們需要的地址的后綴和我們需要傳遞的參數(shù)。
public class Retrofit {
private HttpUrl baseUrl;
private Call.Factory callFactory;
private ConcurrentHashMap<Method,ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();
public Retrofit(Builder builder) {
this.baseUrl = builder.baseUrl;
this.callFactory = builder.callFactory;
}
public HttpUrl baseUrl() {
return baseUrl;
}
public Call.Factory callFactory() {
return callFactory;
}
public <T> T creat(Class<T> clazz){
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//采集數(shù)據(jù)
ServiceMethod serviceMethod = loadServiceMethod(method);
return serviceMethod.toCall(args);
}
});
}
private ServiceMethod loadServiceMethod(Method method){
ServiceMethod serviceMethod = serviceMethodCache.get(method);
if(null == serviceMethod){
//采集
serviceMethod = new ServiceMethod.Builder(this,method).bulid();
serviceMethodCache.putIfAbsent(method,serviceMethod);
}
return serviceMethod;
}
/*
構(gòu)建者模式
*/
public static final class Builder{
private HttpUrl baseUrl;
private Call.Factory callFactory;
public Builder baseUrl(String baseUrl){
this.baseUrl = HttpUrl.parse(baseUrl);
return this;
}
public Builder callFactory(Call.Factory callFactory){
this.callFactory = callFactory;
return this;
}
public Retrofit build(){
if(baseUrl == null){
throw new IllegalArgumentException("baseUrl not set");
}
if(callFactory == null){
callFactory = new OkHttpClient();
}
return new Retrofit(this);
}
}
}
這里有幾點需要說一下:
1、creat方法:其實creat方法主要的功能就是通過動態(tài)代理獲取到接口類的實例,然后通過實例獲取到請求方法的各個參數(shù)(包括注解上面的地址后綴和每個接口需要傳遞的參數(shù));我在這里把這些放到了一個緩存中,這樣也可以加快效率。在
2、callFactory方法:其實retrofit中這種工廠設(shè)計模式使我們想要使用它的理由之一,它的這種插拔式的使用讓我們可以把數(shù)據(jù)轉(zhuǎn)為Gson或者Xml等類型的數(shù)據(jù),并且還可以和RxJava組合使用,這里沒有在向外延伸。
3、因為調(diào)用weather的get方法返回的是Call對象,所以需要在creat方法中的invoke中返回Call對象。這里的Call是okhttp3包下的。
public Call toCall(Object[] args) {
//1.創(chuàng)建Request
Request.Builder requestBuilder = new Request.Builder();
//1.1地址
if (urlBuilder == null) {
urlBuilder = baseUrl.newBuilder(relativeUrl);
}
//1.2如果是get請求,將參數(shù)放到地址中
for (int i = 0; i < parameterHandlers.length; i++) {
parameterHandlers[i].apply(this, String.valueOf(args[i]));
}
if (formBuilder != null) {
formBody = formBuilder.build();
}
requestBuilder.url(urlBuilder.build());
Request request = requestBuilder.method(httpMethod, formBody).build();
//2.創(chuàng)建call
return callFactory.newCall(request);
}
4、因為獲取到方法上面的參數(shù)需要拼接和判斷(這里只做了GET和POST)。所以用了一個serviceMethod來處理這一系列的問題;
public ServiceMethod bulid() {
//處理方法的注解
for (Annotation annotation : methodAnnotation) {
//GET
paseMethodAnnotation(annotation);
}
//處理參數(shù)的注解
parameterHandlers = new ParameterHandler[parameterAnnotations.length];
//遍歷參數(shù)注解
for (int i = 0; i < parameterAnnotations.length; i++) {
Annotation[] parameterAnnotation = parameterAnnotations[i];
//遍歷一個參數(shù)上的左右注解
for (Annotation annotation : parameterAnnotation) {
if (annotation instanceof Query) {
Query query = (Query) annotation;
String value = query.value();
parameterHandlers[i] = new ParameterHandler.Query(value);
} else if (annotation instanceof Field) {
Field field = (Field) annotation;
String value = field.value();
parameterHandlers[i] = new ParameterHandler.Filed(value);
}
}
}
return new ServiceMethod(this);
}
這樣的話我們把地址和參數(shù)都拼裝好了,而且也獲取到Call對象了,然后執(zhí)行call.execute()就可以獲取到Response了。
try {
Response response = call.execute();
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
總結(jié):對于不理解retrofit的人來說,剛開始對于retrofit的用法和原理都是一頭霧水,還有一些人都是停止在會用retrofit的方法之上就沒有深入研究。其實可以簡單的對retrofit理解為:它就是對okhttp的一個封裝,只不過可擴展性更強。內(nèi)部的根本原理就是OkHttp。因為內(nèi)部對于請求方法的類使用了代理模式獲取請求地址和想要傳遞的參數(shù),所以請求方法類必須使用接口,并且在Retrofit的內(nèi)部使用 ConverterFactory工廠類來讓用戶選擇返回數(shù)據(jù)的格式(可以是Gson,也可以是XML等各種數(shù)據(jù)格式),更加的方便靈活。甚至還可以通過CallAdapterFactory的適配器模式來與RxJava組合使用,這就是靈活之處。