带你手撸一个Retrofit
前言
Retrofit是一个优秀的网络请求框架的封装,它本身不请求数据,网络请求的工作由OKhttp负责。现在比较流行的MVP框架通常是由Retrofit+Rxjava+MVP来构建的。任何一款优秀的框架,都有值得我们去学习的亮点。下面我们先看一下Retrofit的使用方法,然后写一个我们自己的Retrofit。
Retrofit的使用
- 定义接口
interface API {
@GET("/ip/ipNew")
Call get(@Query("ip") String ip, @Query("key") String key);
}
- 初始化Retrofit类,获得接口实现
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL).build();
API api = retrofit.create(API.class);
- 调用接口
Call call = api.get(IP, KEY);
Response response = call.execute();
if (response != null && response.body() != null) {
System.out.println("GET请求:" + response.body().string());
}
自定义Retrofit
首先定义四个注解,分别用来描述请求方式和参数类型
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {
String value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface POST {
String value();
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Query {
String value();
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Field {
String value();
}
定义一个Retrofit类,使用建造者模式。
private HttpUrl baseUrl;
private Call.Factory callFactory;
/**
* 缓存请求的方法
*
* @param builder
*/
private final Map<Method, ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();
private Retrofit(Builder builder) {
this.baseUrl = builder.baseUrl;
this.callFactory = builder.callFactory;
}
public HttpUrl getBaseUrl() {
return baseUrl;
}
public Call.Factory getCallFactory() {
return callFactory;
}
public static class Builder {
private HttpUrl baseUrl;
private Call.Factory callFactory;
public Builder baseUrl(String baseUrl) {
if (baseUrl.isEmpty()) {
throw new NullPointerException("baseUrl == null");
}
this.baseUrl = HttpUrl.parse(baseUrl);
return this;
}
public Builder baseUrl(HttpUrl baseUrl) {
if (baseUrl == null) {
throw new NullPointerException("baseUrl == null");
}
this.baseUrl = baseUrl;
return this;
}
public Builder callFactory(Call.Factory callFactory) {
this.callFactory = callFactory;
return this;
}
public Retrofit build() {
if (this.baseUrl == null) {
throw new IllegalStateException("BaseUrl required.");
}
if (this.callFactory == null) {
callFactory = new OkHttpClient();
}
return new Retrofit(this);
}
}
其中serviceMethodCache
是用来缓存接口类定义的方法,以方法Method
为key,ServiceMethod
为value,ServiceMethod
是请求方法属性的封装类,该类中定义了解析Method
中所有参数信息的方法,同样使用建造者模式。下面是其实现:
public class ServiceMethod {
//OKHTTPClient唯一实现接口
private final Call.Factory callFactory;
//接口请求的地址
private final HttpUrl baseUrl;
//方法请求方式
private final String httpMethod;
//方法的注解的值("/ip/ipNew")
private final String relativeUrl;
//方法参数的数组
private final ParameterHandler[] parameterHandlers;
//是否有请求体
private final boolean hasBody;
private ServiceMethod(Builder builder) {
this.callFactory = builder.retrofit.getCallFactory();
this.baseUrl = builder.retrofit.getBaseUrl();
this.httpMethod = builder.httpMethod;
this.relativeUrl = builder.relativeUrl;
this.parameterHandlers = builder.parameterHandlers;
this.hasBody = builder.hasBody;
}
okhttp3.Call toCall(Object... args){
RequestBuilder requestBuilder = new RequestBuilder(httpMethod,baseUrl,relativeUrl,hasBody);
ParameterHandler[] handlers = this.parameterHandlers;
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length){
throw new IllegalArgumentException("");
}
for (int i = 0; i < argumentCount; i++) {
handlers[i].apply(requestBuilder,args[i].toString());
}
//创建请求
return callFactory.newCall(requestBuilder.build());
}
static final class Builder {
final Retrofit retrofit;
//带注解的方法
final Method method;
//方法的所有注解
final Annotation[] methodAnnotations;
//方法参数的所有注解
final Annotation[][] parameterAnnotationsArray;
//方法的请求方式get post
private String httpMethod;
//方法注解的值
private String relativeUrl;
//方法参数的数组(每个对象包含:参数注解值、参数值)
private ParameterHandler[] parameterHandlers;
//是否有请求体
private boolean hasBody;
public Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
ServiceMethod build() {
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
//定义方法参数的数组长度
int parameterCount = parameterAnnotationsArray.length;
//初始化方法参数的数组
parameterHandlers = new ParameterHandler[parameterCount];
for (int i = 0; i < parameterCount; i++) {
//获取方法的每个参数的多个注解
Annotation[] parameterAnnotations = parameterAnnotationsArray[i];
if (parameterAnnotations == null) {
throw new NullPointerException("参数无注解");
}
parameterHandlers[i] = parseParameter(i, parameterAnnotations);
}
return new ServiceMethod(this);
}
//解析参数的所有注解 嵌套循环
private ParameterHandler parseParameter(int i, Annotation[] annotations) {
ParameterHandler result = null;
for (Annotation annotation : annotations) {
ParameterHandler annotationAction = parseParameterAnnotation(annotation);
if (annotationAction == null) {
continue;
}
result = annotationAction;
}
if (result == null) {
throw new IllegalArgumentException("没有Retrofit注解的支持");
}
return result;
}
//解析参数的注解
private ParameterHandler parseParameterAnnotation(Annotation annotation) {
if (annotation instanceof Query) {
Query query = (Query) annotation;
//参数注解的值
String name = query.value();
return new ParameterHandler.Query(name);
} else if (annotation instanceof Field) {
Field field = (Field) annotation;
String name = field.value();
return new ParameterHandler.Field(name);
}
return null;
}
/**
* 解析方法的注解 GET/POST
*
* @param annotation 注解
*/
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
}
}
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
this.httpMethod = httpMethod;
this.relativeUrl = value;
this.hasBody = hasBody;
}
}
}
实例化Retrofit后,调用create(Class<T> service)
方法,通过动态代理,解析接口方法参数信息,获取到请求方式、请求参数名、请求参数值等,最终通过OKhttp创建网络请求。create(Class<T> service)
方法的实现如下:
public <T> T create(Class<T> service) {
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ServiceMethod serviceMethod = loadServiceMethod(method);
return new OKHttpCall(serviceMethod, args);
}
});
}
/**
* 获得方法中所有内容,包括方法名、注解、参数、参数注解等
*
* @param method
* @return
*/
private ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result = serviceMethodCache.get(method);
if (result != null) {
return 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.Builder()
获取并实例化方法参数信息ServiceMethod
,缓存在serviceMethodCache
中。
然后在代理方法处理中,创建一个OKhttp请求,return new OKHttpCall(serviceMethod, args)
。我们看一下OKhttpCall
的实现:
public class OKHttpCall implements Call {
private ServiceMethod serviceMethod;
private Object[] args;
private okhttp3.Call rawCall;
public OKHttpCall(ServiceMethod serviceMethod, Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
this.rawCall = serviceMethod.toCall(args);
}
@Override
public Request request() {
return rawCall.request();
}
@Override
public Response execute() throws IOException {
return rawCall.execute();
}
@Override
public void enqueue(Callback responseCallback) {
rawCall.enqueue(responseCallback);
}
@Override
public void cancel() {
rawCall.cancel();
}
@Override
public boolean isExecuted() {
return rawCall.isExecuted();
}
@Override
public boolean isCanceled() {
return rawCall.isCanceled();
}
@Override
public Timeout timeout() {
return rawCall.timeout();
}
@Override
public Call clone() {
return new OKHttpCall(serviceMethod,args);
}
}
可以看到,在这里实际上我们使用了ServiceMethod.toCall()
方法生成的okhttp3.Call来全权处理网络访问。我们再来看一下这个toCall()
方法
okhttp3.Call toCall(Object... args) {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, hasBody);
ParameterHandler[] handlers = this.parameterHandlers;
int argumentCount = args != null ? args.length : 0;
//方法真实的参数个数是否等于收集的参数个数
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("");
}
//循环拼接每个参数名+参数值
for (int i = 0; i < argumentCount; i++) {
handlers[i].apply(requestBuilder, args[i].toString());
}
//创建请求
return callFactory.newCall(requestBuilder.build());
}
我们知道,这里的callFactory
是默认的OkHttpClient
,所以这里的newCall(requestBilder.build())
,实际上就是调用了OKHttpClient
中的这个方法,我们看一下这个方法的实现:
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
所以这里创建的是官方的默认Call
接口的实现类RealCall
。
其中ParameterHandler
是用来处理请求参数类型,如Query
、Field
等,它的apply()
方法中调用RequestBuilder
中拼接参数的方法,RequestBuilder
是最终拼接参数的操作类。ParameterHandler
的实现如下:
abstract class ParameterHandler {
/**
* 抽象方法 外部复制和调用,自己的内部类实现了
*
* @param builder 请求构建者(拼装者)
* @param value 方法的参数值
*/
abstract void apply(RequestBuilder builder, String value);
static final class Query extends ParameterHandler{
//参数名
private String name;
Query(String name){
if (name.isEmpty()){
throw new NullPointerException("");
}
this.name = name;
}
@Override
void apply(RequestBuilder builder, String value) {
//此处的value是参数值
if (value == null){
return;
}
builder.addQueryParam(name,value);
}
}
static final class Field extends ParameterHandler{
private final String name;
Field(String name){
if (name.isEmpty()){
throw new NullPointerException("");
}
this.name = name;
}
@Override
void apply(RequestBuilder builder, String value) {
if (value == null){
return;
}
//拼接Field参数,此处name为参数注解的值,value为参数值
builder.addFormField(name,value);
}
}
}
它本身是个抽象类,抽象类中定义了两个自己的实现类。RequestBuilder
的实现如下:
public class RequestBuilder {
private final String method;
private final HttpUrl baseUrl;
private String relativeUrl;
private HttpUrl.Builder urlBuilder;
private FormBody.Builder formBuilder;
private final Request.Builder requestBuilder;
public RequestBuilder(String method, HttpUrl baseUrl, String relativeUrl, boolean hasBody) {
this.method = method;
this.baseUrl = baseUrl;
this.relativeUrl = relativeUrl;
requestBuilder = new Request.Builder();
if (hasBody) {
formBuilder = new FormBody.Builder();
}
}
public void addQueryParam(String name, String value) {
if (relativeUrl != null) {
//拼接访问地址
urlBuilder = baseUrl.newBuilder(relativeUrl);
relativeUrl = null;
}
urlBuilder.addQueryParameter(name, value);
}
public void addFormField(String name, String value) {
formBuilder.add(name, value);
}
Request build() {
HttpUrl url;
if (urlBuilder != null) {
url = urlBuilder.build();
} else {
url = baseUrl.resolve(relativeUrl);
if (url == null) {
throw new IllegalArgumentException("Malformed URL. Base: " + baseUrl + ",Relative:" + relativeUrl);
}
}
RequestBody body = null;
if (formBuilder != null) {
body = formBuilder.build();
}
//构建完整请求
return requestBuilder
.url(url)
.method(method, body)
.build();
}
}
可以看到,在上面ServiceMethod
中的toCall()
方法中返回的callFactory.newCall(requestBuilder.build())
中,newCall(Request request)
方法的参数request
最终就是调用了这里来生成。
return requestBuilder
.url(url)
.method(method, body)
.build();
到这里,我们的仿写就完成了。
总结一下,在Retrofit
中的create(Class<T> service)
方法中,loadServiceMethod(method)
方法收集接口API中定义方法的所有参数信息,new OKHttpCall(serviceMethod, args)
中处理接口的参数,创建代理对象并返回,当执行
Call call = api.get(IP, KEY);
的时候,调用代理对象相应的方法执行。
测试
使用第二步Retrofit的使用
中的代码来测试,打印数据:
GET请求:{"resultcode":"200","reason":"查询成功","result":{"Country":"美国","Province":"加利福尼亚","City":"","Isp":""},"error_code":0}
测试成功,示例代码已上传Github。
后记
不知道大家有没有一个疑问,我上面直接使用Retrofit
来请求数据,定义的接口是这样的:
@GET("/ip/ipNew")
Call get(@Query("ip") String ip, @Query("key") String key);
然而在与Rxjava
结合使用的时候,是这样定义的:
@GET("/ip/ipNew")
Observable<Bean> get(@Query("ip") String ip, @Query("key") String key);
返回类型不一样,而上面我们在实现中,代理对象是RealCall
,那这样的话,就不可能返回Observalbe
类型啊?
OK,那就让我们来看一下,为个啥。
先来看一下官方Retrofit
中create(Class<T> service)
方法的实现:
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, @Nullable 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<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
看最后一句
return serviceMethod.callAdapter.adapt(okHttpCall);
这里返回的类型是由callAdapter
来决定的。很明显这是个请求适配器,它的初始化工作在ServiceMethod
的build()
函数中
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
...
我们看一下这个createCallAdapter()
方法:
private CallAdapter<T, R> 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 {
//noinspection unchecked
return (CallAdapter<T, R>) 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);
}
}
可以看到是调用了retrofit.callAdapter()
方法。而retrofit中的callAdapter的初始化时机在Retrofit的nextCallAdapter
方法中:
public CallAdapter<?, ?> nextCallAdapter(@Nullable 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;
}
}
...
是拿adapterFactories
获取的,这是个请求适配器工厂的集合,定义如下
final List<CallAdapter.Factory> adapterFactories;
那这个集合中的数据何时添加的呢?这里我们回想一下,大家在使用Retrofit+Rxjava的时候,是怎么配置Retrofit的?是不是下面这样:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
看到了吗?是我们添加了RxJavaCallAdapterFactory
这个适配器工厂,所以最终定义接口的时候,可以返回Observable类型。这个RxJava2CallAdapterFactory
的支持库,正是大神Jake Wharton
的杰作之一。
什么?这人是谁不认识?不怕,咱现场百度,感受一下大佬气息
啥也不说了,渣渣的我瑟瑟发抖
上一篇: 深入了解哈希表操作(开放定址法-拉链法)