Retrofit原理:
要理解Retrofit的原理,我们首先要知道 注解 和 动态代理
1.注解(annotation)
我们最常见的注解是java内置的@Override注解:
加上此注解表示该方法是重写父类的方法,方法签名错误时,编译器会发出错误提示。
这说明@Override注解在编译期就起了作用,按住ctrl,鼠标左键点进@Override:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@Target是作用于注解上的注解,表示该注解作用的目标(一个方法或是一个域)
@Retention表明该注解的作用域:
1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被编译器遗弃; 2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期; 3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,因此可以通过反射机制读取注解的信息;
生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。
我们来看Retrofit给出的,写接口的例子:
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(@Path("owner") String owner, @Path("repo") String repo);
}
使用过Retrofit的人知道,该接口方法会被转换成调用网络请求的方法。
看看@GET:
/** Make a GET request. */
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {
/**
* A relative or absolute path, or full URL of the endpoint. This value is optional if the first
* parameter of the method is annotated with {@link Url @Url}.
*
* <p>See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how
* this is resolved against a base URL to create the full endpoint URL.
*/
String value() default "";
}
说明该注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。
2.动态代理
静态代理:
public interface IProxy {
void hello();
void bye();
}
public class Greet implements IProxy{
@Override
public void hello() {
System.out.print("hello");
}
@Override
public void bye() {
System.out.print("bye");
}
}
public class GreetToWorld implements IProxy{
public GreetToWorld(IProxy greet) {
this.greet = greet;
}
IProxy greet;
@Override
public void hello() {
greet.hello();
System.out.println(",world");
}
@Override
public void bye() {
greet.bye();
System.out.println(",world");
}
}
public class Test {
public static void main(String[] args) {
Greet greet = new Greet();
//静态代理
GreetToWorld greetToWorld = new GreetToWorld(greet);
greetToWorld.hello();
greetToWorld.bye();
}
}
输出:
静态代理的缺点是每一个被代理的类都需要新建一个代理类,尽管这个代理类只是都只是增加了相同的逻辑。
我们需要生成一个代理对象,调用这个对象的方法A,就等于调用被代理对象的方法A,然后加上自己的逻辑B。我们需要代理对象的ClassLoader,和代理对象需要被代理的方法(用接口表示),然后自定义自己的逻辑B。
动态代理:
public class Test {
public static void main(String[] args) {
Greet greet = new Greet();
//动态代理hello
IProxy dynamicProxy = (IProxy) Proxy.newProxyInstance(Greet.class.getClassLoader(), new Class<?>[]{IProxy.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = method.invoke(greet, args);//在这里调用被代理对象的方法A
System.out.print("world! ");//在这里加上自己的逻辑B
return res;
}
});
dynamicProxy.hello();
dynamicProxy.bye();
}
}
输出:
再看Retrofit:
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(@Path("owner") String owner, @Path("repo") String repo);
}
Retrofit将这个GitHub接口中的方法,转换成网络请求,需要先新建一个Retrofit实例(后面讲如何配置),然后调用GitHub github = retrofit.create(GitHub.class)
// Create a very simple REST adapter which points the GitHub API.
Retrofit retrofit =
new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
// Create an instance of our GitHub API interface.
GitHub github = retrofit.create(GitHub.class);
需要进行网络请求时,使用:
// Create a call instance for looking up Retrofit contributors.
Call<List<Contributor>> call = github.contributors("square", "retrofit");
// Fetch and print a list of the contributors to the library.
List<Contributor> contributors = call.execute().body();
第一步传入参数,新建一个Call对象:Call用来发送网络请求,每个Call产生一对自己的<Request, Response>,可以用clone方法新建一个参数和请求地址都相同的Call来实现轮询(poll)或者重传(retry)。用execute()发送一个同步请求,用enqueue()发送一个异步请求。
github.contributors("square", "retrofit")调用的肯定不是GitHub接口中的contributors方法,接口方法无法调用。此时调用的是被代理的GitHub接口中的contributors方法,所有的接口都被动态代理用同一个模板代理为网络请求,代理过程在GitHub github = retrofit.create(GitHub.class)中实现:
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable 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);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
首先用validateServiceInterface(service)验证传入的service是一个接口,然后返回一个代理对象,这个代理对象把所有请求变成loadServiceMethod(method).invoke(args)。
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
这里有一个缓存,没缓存的话就调用ServiceMethod.parseAnnotations(this, method)解析接口的Annotation:
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
} else if (annotation instanceof PATCH) {
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof OPTIONS) {
parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} else if (annotation instanceof retrofit2.http.Headers) {
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
if (headersToParse.length == 0) {
throw methodError(method, "@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
在这里把注解解析为请求的方法。
Retrofit只负责生产对象,生产能做网络请求的工作对象,他有点像一个工厂,只提供产品,工厂本身不处理网络请求,产品才能处理网络请求。
Retrofit使用OkHttpClient来实现网络请求,这个OkHttpClient虽然不能替换为其他的网络执行框架比如Volley,但是Retrofit允许我们使用自己扩展OkHttpClient,一般最常扩展的就是Interceptor拦截器了。
扩展的是对返回的数据类型的自动转换,把一种数据对象转换为另一种数据对象。 在上述场景中,GsonConverterFactory可以把Http访问得到的json字符串转换为Java数据对象BizEntity。
扩展的是对网络工作对象callWorker的自动转换,把Retrofit中执行网络请求的Call对象,转换为接口中定义的Call对象,例如Rxjava的Observable对象。
OKHTTP的特点:
-
支持 Http2.0,允许同一主机的所有请求共享套接字。
- Http2.0:安全是因为http2.0建立在https协议的基础上,高效是因为它是通过二进制分帧来进行数据传输
-
使用连接池减少请求延迟(如果HTTP / 2不可用)。
-
透明GZIP缩小下载大小。
-
响应缓存可以避免重复请求的网络。
- OKHttp 提供了缓存机制以将我们的的 HTTP 和 HTTPS 请求的响应缓存到文件系统中
- 构造 OkHttpClient 时配置 Cache,设置缓存路径已经缓存大小
- 构造 Request 时配置 CacheControl,配置缓存相关属性
-
内部实现任务队列,提高并发访问的效率。
-
实现拦截器链(InterceptorChain),实现request与response的分层处理
-
OkHttp 也提供了对 WebSocket 的支持。
- HTTP 协议有一个缺陷:通信只能由客户端发起
- WebSocket : 服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于[“服务器推送技术”]的一种
- 设置pingInterval会定时的向服务器发送一个消息来保持长连接
同步请求:
Response response = mOkHttpClient.newCall(okRequest).execute();
异步请求:WebSocke长连接WebSocket在TCP连接建立后,还要通过Http进行一次握手,也就是通过Http发送一条GET请求消息给服务器,告诉服务器我要建立WebSocket连接了,你准备好哦,具体做法就是在头部信息中添加相关参数。然后服务器响应我知道了,并且将连接协议改成WebSocket,开始建立长连接。
- URL一般是以
ws或者wss开头,ws对应Websocket协议,wss对应在TLS之上的WebSocket。类似于Http和Https的关系,如wss://192.168.1.16。
OKHTTP的响应头
final class RealCall implements Call {
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
//...
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
try {
//进行网络请求
client.dispatcher().executed(this);
//经过一层层网络拦截器之后,获取网络请求的返回值
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
//Application拦截器
interceptors.addAll(client.interceptors());
//重定向和失败后重新请求拦截器
interceptors.add(retryAndFollowUpInterceptor);
//网桥拦截器,顾名思义client和Server之前的桥梁
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//缓存处理拦截器
interceptors.add(new CacheInterceptor(client.internalCache()));
//Socket层的握手链接
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//网络拦截器,我们可以自定义这个拦截器,比放在前面获得更多信息
interceptors.addAll(client.networkInterceptors());
}
//client和Server之前的读写操作
interceptors.add(new CallServerInterceptor(forWebSocket));
//责任链开始执行
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
}
\