// 如果 result 不为 null 说明之前遍历时已经找到了 SimpleRetrofit 能处理的注解
// 不允许一个参数上被多个 SimpleRetrofit 的参数注解标注,抛异常
if (result != null) {
throw new IllegalArgumentException(“Multiple Retrofit annotations found, only one allowed.”);
}
result = annotationAction;
}
// 遍历完都没找到说明这个参数没有被 SimpleRetrofit 注解标注,不应该被检查
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) {
// 规定一个方法上只能有一个 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() 中负责解析方法注解和方法参数注解。解析方法注解主要是为了获取使用哪种 HTTP 方法(GET、POST)、是否有请求体以及相对地址;解析方法参数注解是为了把被 @Field 或 @Query 注解的参数的值,即网络请求的 key 保存在 ParameterHandler[] 中。比如说对于 getWeather():
@GET(“/v3/weather/weatherInfo”)
Call getWeather(@Query(“city”) String city, @Query(“key”) String key);
@Query 注解的值分别为 city、key,那么就把 city 和 key 分别传入 ParameterHandler[] 中保存,而这两个 key 对应的 value 会在调用 ServiceMethod 的 invoke() 方法时传入。
ParameterHandler 的作用是保存网络请求的 key,并把 key-value 回调给 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);
}
}
}
回调方法一个是处理 GET 请求的,一个是处理 POST 请求的:
// get 请求,把 key-value 拼接到 url 中
public void addQueryParameter(String key, String value) {
if (urlBuilder == null) {
urlBuilder = baseUrl.newBuilder(relativeUrl);
}
urlBuilder.addQueryParameter(key, value);
}
// post 请求,把 key-value 放到请求体中
public void addFieldParameter(String key, String value) {
formBuilder.add(key, value);
}
最后在 invoke() 中生成 OKHttp 的 Request 对象并调用 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);
}
2、使用
====
注解见上,不重复写了。
2.1、SimpleRetrofit
package com.test.retrofittest2.retro;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import okhttp3.Call;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
public class SimpleRetrofit {
HttpUrl baseUrl;
Call.Factory callFactory;
private final Map<Method, ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();
public SimpleRetrofit(Builder builder) {
this.baseUrl = builder.baseUrl;
this.callFactory = builder.callFactory;
}
public static class Builder {
HttpUrl baseUrl;
Call.Factory callFactory;
public Builder baseUrl(String baseUrl) {
this.baseUrl = HttpUrl.parse(baseUrl);
return this;
}
public SimpleRetrofit build() {
// 先做参数校验
if (baseUrl == null) {
throw new IllegalStateException(“Base URL required.”);
}
if (callFactory == null) {
callFactory = new OkHttpClient();
}
return new SimpleRetrofit(this);
}
}
@SuppressWarnings(“unchecked”)
public T create(final Class 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,如果没有解析过就解析该方法
-
@param method 动态代理执行的接口方法
-
@return 解析后的方法对象
*/
private ServiceMethod loadServiceMethod(Method method) {
// 先不加锁,避免性能损失
ServiceMethod serviceMethod = serviceMethodCache.get(method);
if (serviceMethod != null) return serviceMethod;
// 避免多线程下重复解析
synchronized (serviceMethodCache) {
serviceMethod = serviceMethodCache.get(method);
if (serviceMethod == null) {
serviceMethod = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, serviceMethod);
}
}
return serviceMethod;
}
}
2.2、ServiceMethod
package com.test.retrofittest2.retro;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.Request;
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;
// 如果有请求体,创建一个 OKHttp 请求体对象
if (hasBody) {
formBuilder = new FormBody.Builder();
}
}
static class Builder {
private final SimpleRetrofit retrofit;
private final Method method;
private final Annotation[] annotations;
// 方法上有 n 个参数,每个参数又有 m 个注解,用一个 nxm 的数组保存
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);
}
}
// 解析方法参数上的所有注解,把注解值存入 ParameterHandler[] 中
int length = parameterAnnotations.length; // 方法上的参数个数
parameterHandlers = new ParameterHandler[length];
for (int i = 0; i < length; i++) {
// 一个参数上的所有注解
Annotation[] annotations = parameterAnnotations[i];
parameterHandlers[i] = parseParameter(annotations);
}
return new ServiceMethod(this);
}
private ParameterHandler parseParameter(Annotation[] annotations) {
// 根据注解类型创建对应的 ParameterHandler
ParameterHandler result = null;
for (Annotation annotation : annotations) {
ParameterHandler annotationAction = parseParameterAction(annotation, annotations);
// 如果当前检查的注解并不是我们能处理的,就继续遍历下一个
if (annotationAction == null) {
continue;
}
// 如果 result 不为 null 说明之前遍历时已经找到了 SimpleRetrofit 能处理的注解
// 不允许一个参数上被多个 SimpleRetrofit 的参数注解标注,抛异常
if (result != null) {
throw new IllegalArgumentException(“Multiple Retrofit annotations found, only one allowed.”);
}
result = annotationAction;
}
// 遍历完都没找到说明这个参数没有被 SimpleRetrofit 注解标注,不应该被检查
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) {
// 规定一个方法上只能有一个 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;
}
}
// get 请求,把 key-value 拼接到 url 中
public void addQueryParameter(String key, String value) {
if (urlBuilder == null) {
urlBuilder = baseUrl.newBuilder(relativeUrl);
}
urlBuilder.addQueryParameter(key, value);
}
// post 请求,把 key-value 放到请求体中
public void addFieldParameter(String key, String value) {
formBuilder.add(key, value);
}
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);
}
}
2.3、ParameterHandler
package com.test.retrofittest2.retro;
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);
}
}
}
2.4、Api
package com.test.retrofittest2;
import com.test.retrofittest2.retro.Field;
import com.test.retrofittest2.retro.GET;
import com.test.retrofittest2.retro.POST;
import com.test.retrofittest2.retro.Query;
public interface Api {
@GET(“/v3/weather/weatherInfo”)
okhttp3.Call getWeather(@Query(“city”) String city, @Query(“key”) String key);
@POST(“/v3/weather/weatherInfo”)
okhttp3.Call postWeather(@Field(“city”) String city, @Field(“key”) String key);
}
2.5、MainActivity
package com.test.retrofittest2;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.test.retrofittest2.retro.SimpleRetrofit;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
private static final String TAG = “MainActivity”;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_get).setOnClickListener(view -> getRequest());
findViewById(R.id.btn_post).setOnClickListener(view -> postRequest());
}
private void postRequest() {
SimpleRetrofit retrofit = new SimpleRetrofit.Builder().baseUrl(“https://restapi.amap.com”).build();
Api api = retrofit.create(Api.class);
Call getCall = api.postWeather(“北京”, “13cb58f5884f9749287abbead9c658f2”);
getCall.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(TAG, "onResponse: " + response.body().string());
}
});
}
private void getRequest() {
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 {
Log.i(TAG, "onResponse: " + response.body().string());
}
});
}
}
2.6、布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android=“http://schemas.android.com/apk/res/android”
xmlns:app=“http://schemas.android.com/apk/res-auto”
xmlns:tools=“http://schemas.android.com/tools”
android:layout_width=“match_parent”
写在最后
在技术领域内,没有任何一门课程可以让你学完后一劳永逸,再好的课程也只能是“师傅领进门,修行靠个人”。“学无止境”这句话,在任何技术领域,都不只是良好的习惯,更是程序员和工程师们不被时代淘汰、获得更好机会和发展的必要前提。
如果你觉得自己学习效率低,缺乏正确的指导,可以一起学习交流!
加入我们吧!群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。
35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!