一混滔、Retrofit 基本使用
這里只實(shí)現(xiàn)最基本的使用烹笔,適配器和轉(zhuǎn)換器等并未使用伙窃。
先定義 Api 接口:
public interface Api {
@GET("/v3/weather/weatherInfo")
Call getWeather(@Query("city") String city, @Query("key") String key);
@POST("/v3/weather/weatherInfo")
Call postWeather(@Field("city") String city, @Field("key") String key);
}
分別使用 POST 和 GET 請(qǐng)求去獲取天氣數(shù)據(jù),這里使用的是高德地圖的 API颅夺。
接下來(lái)創(chuàng)建 Retrofit 對(duì)象朋截,獲取 Api 實(shí)例,用異步方式執(zhí)行 Api 中的請(qǐng)求:
private void request() {
SimpleRetrofit retrofit = new SimpleRetrofit.Builder().baseUrl("https://restapi.amap.com").build();
Api api = retrofit.create(Api.class);
Call getCall = api.getWeather("北京", "13cb58f5884f9749287abbead9c658f2");
getCall.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println(response.body().string());
}
});
}
二吧黄、手寫(xiě)代碼
寫(xiě)代碼之前要了解一個(gè)前提部服,就是 Retrofit 其實(shí)是通過(guò)封裝 OKHttp 才擁有了網(wǎng)絡(luò)訪問(wèn)能力的,實(shí)際執(zhí)行網(wǎng)絡(luò)請(qǐng)求的是 OKHttp拗慨。Retrofit 要做的是為網(wǎng)絡(luò)請(qǐng)求接口生成動(dòng)態(tài)代理對(duì)象廓八,并在請(qǐng)求方法被調(diào)用時(shí),在動(dòng)態(tài)代理的 InvocationHandler 中解析注解赵抢,把要使用的網(wǎng)絡(luò)請(qǐng)求方法和參數(shù)解析出來(lái)生成 OKHttp 的 Request 對(duì)象剧蹂,最后由 OKHttp 發(fā)送請(qǐng)求。
我們要實(shí)現(xiàn)的是一個(gè)極簡(jiǎn)版的 Retrofit(只是為了輔助更好的理解 Retrofit 框架)烦却,請(qǐng)求方法只實(shí)現(xiàn)了 GET宠叼、POST,參數(shù)注解只支持 @Query其爵、@Field:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GET {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface POST {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Query {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Field {
String value();
}
Retrofit 使用 Builder 創(chuàng)建其對(duì)象:
public class SimpleRetrofit {
HttpUrl baseUrl;
Call.Factory callFactory;
public SimpleRetrofit(Builder builder) {
this.baseUrl = builder.baseUrl;
this.callFactory = builder.callFactory;
}
static class Builder {
HttpUrl baseUrl;
Call.Factory callFactory;
Builder baseUrl(String baseUrl) {
this.baseUrl = HttpUrl.parse(baseUrl);
return this;
}
public SimpleRetrofit build() {
// 先做參數(shù)校驗(yàn)
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
if (callFactory == null) {
callFactory = new OkHttpClient();
}
return new SimpleRetrofit(this);
}
}
}
Call.Factory 是生成 Call 對(duì)象的工廠冒冬,其唯一實(shí)現(xiàn)類為 OKHttpClient,調(diào)用其 newCall(Request) 方法可以生成 Call 對(duì)象摩渺,最后要通過(guò)這個(gè)方法把我們解析出來(lái)的數(shù)據(jù)封裝在 Request 中以生成 Call 對(duì)象简烤。
MyRetrofit 對(duì)象生成后,通過(guò)調(diào)用 create() 生成接口對(duì)象摇幻。很明顯是在 create() 中為接口生成了動(dòng)態(tài)代理横侦,同時(shí)做了接口方法的解析和調(diào)用:
private final Map<Method, ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();
public <T> T create(final Class<T> service) {
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service},
new InvocationHandler() {
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
ServiceMethod serviceMethod = loadServiceMethod(method);
return serviceMethod.invoke(args);
}
});
}
/**
* DLC 方式獲取 ServiceMethod挥萌,如果沒(méi)有解析過(guò)就解析該方法
* @param method 動(dòng)態(tài)代理執(zhí)行的接口方法
* @return 解析后的方法對(duì)象
*/
private ServiceMethod loadServiceMethod(Method method) {
// 先不加鎖,避免性能損失
ServiceMethod serviceMethod = serviceMethodCache.get(method);
if (serviceMethod != null) return serviceMethod;
// 避免多線程下重復(fù)解析
synchronized (serviceMethodCache) {
serviceMethod = serviceMethodCache.get(method);
if (serviceMethod == null) {
serviceMethod = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, serviceMethod);
}
}
return serviceMethod;
}
在 ServiceMethod 的 Builder 中解析方法和參數(shù)注解:
public class ServiceMethod {
private final String httpMethod;
private final Call.Factory callFactory;
private final HttpUrl baseUrl;
private final String relativeUrl;
private final ParameterHandler[] parameterHandlers;
private FormBody.Builder formBuilder;
private FormBody formBody;
private HttpUrl.Builder urlBuilder;
public ServiceMethod(Builder builder) {
baseUrl = builder.retrofit.baseUrl;
callFactory = builder.retrofit.callFactory;
httpMethod = builder.httpMethod;
relativeUrl = builder.relativeUrl;
parameterHandlers = builder.parameterHandlers;
boolean hasBody = builder.hasBody;
// 如果有請(qǐng)求體枉侧,創(chuàng)建一個(gè) OKHttp 請(qǐng)求體對(duì)象
if (hasBody) {
formBuilder = new FormBody.Builder();
}
}
static class Builder {
private final SimpleRetrofit retrofit;
private final Method method;
private final Annotation[] annotations;
// 方法上有 n 個(gè)參數(shù)引瀑,每個(gè)參數(shù)又有 m 個(gè)注解,用一個(gè) nxm 的數(shù)組保存
private final Annotation[][] parameterAnnotations;
String httpMethod;
boolean hasBody;
String relativeUrl;
ParameterHandler[] parameterHandlers;
public Builder(SimpleRetrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
annotations = method.getAnnotations();
parameterAnnotations = method.getParameterAnnotations();
}
public ServiceMethod build() {
// 解析方法上的注解
for (Annotation annotation : annotations) {
if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
}
}
// 解析方法參數(shù)上的所有注解榨馁,把注解值存入 ParameterHandler[] 中
int length = parameterAnnotations.length; // 方法上的參數(shù)個(gè)數(shù)
parameterHandlers = new ParameterHandler[length];
for (int i = 0; i < length; i++) {
// 一個(gè)參數(shù)上的所有注解
Annotation[] annotations = parameterAnnotations[i];
parameterHandlers[i] = parseParameter(annotations);
}
return new ServiceMethod(this);
}
private ParameterHandler parseParameter(Annotation[] annotations) {
// 根據(jù)注解類型創(chuàng)建對(duì)應(yīng)的 ParameterHandler
ParameterHandler result = null;
for (Annotation annotation : annotations) {
ParameterHandler annotationAction = parseParameterAction(annotation, annotations);
// 如果當(dāng)前檢查的注解并不是我們能處理的伤疙,就繼續(xù)遍歷下一個(gè)
if (annotationAction == null) {
continue;
}
// 如果 result 不為 null 說(shuō)明之前遍歷時(shí)已經(jīng)找到了 SimpleRetrofit 能處理的注解
// 不允許一個(gè)參數(shù)上被多個(gè) SimpleRetrofit 的參數(shù)注解標(biāo)注,拋異常
if (result != null) {
throw new IllegalArgumentException("Multiple Retrofit annotations found, only one allowed.");
}
result = annotationAction;
}
// 遍歷完都沒(méi)找到說(shuō)明這個(gè)參數(shù)沒(méi)有被 SimpleRetrofit 注解標(biāo)注辆影,不應(yīng)該被檢查
if (result == null) {
throw new IllegalArgumentException("No Retrofit annotation found.");
}
return result;
}
private ParameterHandler parseParameterAction(Annotation annotation, Annotation[] annotations) {
if (annotation instanceof Query) {
String key = ((Query) annotation).value();
return new ParameterHandler.QueryParameterHandler(key);
} else if (annotation instanceof Field) {
String key = ((Field) annotation).value();
return new ParameterHandler.FieldParameterHandler(key);
}
return null;
}
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
// 規(guī)定一個(gè)方法上只能有一個(gè) httpMethod 注解,否則拋出異常
if (this.httpMethod != null) {
String message = String.format("Only one HTTP method is allowed. Found: %s and %s.",
this.httpMethod, httpMethod);
throw new IllegalArgumentException(message);
}
this.httpMethod = httpMethod;
this.hasBody = hasBody;
if (value == null) {
return;
}
this.relativeUrl = value;
}
}
}
build() 中負(fù)責(zé)解析方法注解和方法參數(shù)注解黍特。解析方法注解主要是為了獲取使用哪種 HTTP 方法(GET蛙讥、POST)、是否有請(qǐng)求體以及相對(duì)地址灭衷;解析方法參數(shù)注解是為了把被 @Field 或 @Query 注解的參數(shù)的值次慢,即網(wǎng)絡(luò)請(qǐng)求的 key 保存在 ParameterHandler[] 中。比如說(shuō)對(duì)于 getWeather():
@GET("/v3/weather/weatherInfo")
Call getWeather(@Query("city") String city, @Query("key") String key);
@Query 注解的值分別為 city翔曲、key迫像,那么就把 city 和 key 分別傳入 ParameterHandler[] 中保存,而這兩個(gè) key 對(duì)應(yīng)的 value 會(huì)在調(diào)用 ServiceMethod 的 invoke() 方法時(shí)傳入瞳遍。
ParameterHandler 的作用是保存網(wǎng)絡(luò)請(qǐng)求的 key闻妓,并把 key-value 回調(diào)給 ServiceMethod:
public abstract class ParameterHandler {
abstract void apply(ServiceMethod serviceMethod, String value);
static class QueryParameterHandler extends ParameterHandler {
String key;
public QueryParameterHandler(String key) {
this.key = key;
}
@Override
void apply(ServiceMethod serviceMethod, String value) {
serviceMethod.addQueryParameter(key, value);
}
}
static class FieldParameterHandler extends ParameterHandler {
String key;
public FieldParameterHandler(String key) {
this.key = key;
}
@Override
void apply(ServiceMethod serviceMethod, String value) {
serviceMethod.addFieldParameter(key, value);
}
}
}
回調(diào)方法一個(gè)是處理 GET 請(qǐng)求的,一個(gè)是處理 POST 請(qǐng)求的:
// get 請(qǐng)求掠械,把 key-value 拼接到 url 中
public void addQueryParameter(String key, String value) {
if (urlBuilder == null) {
urlBuilder = baseUrl.newBuilder(relativeUrl);
}
urlBuilder.addQueryParameter(key, value);
}
// post 請(qǐng)求由缆,把 key-value 放到請(qǐng)求體中
public void addFieldParameter(String key, String value) {
formBuilder.add(key, value);
}
最后在 invoke() 中生成 OKHttp 的 Request 對(duì)象并調(diào)用 CallFactory 的 newCall(Request) 生成 Call:
public Object invoke(Object[] args) {
for (int i = 0; i < parameterHandlers.length; i++) {
ParameterHandler parameterHandler = parameterHandlers[i];
parameterHandler.apply(this, args[i].toString());
}
if (urlBuilder == null) {
urlBuilder = baseUrl.newBuilder(relativeUrl);
}
HttpUrl httpUrl = urlBuilder.build();
if (formBuilder != null) {
formBody = formBuilder.build();
}
Request request = new Request.Builder().url(httpUrl).method(httpMethod, formBody).build();
return callFactory.newCall(request);
}