Retrofit 是一個(gè) RESTful 的 HTTP 網(wǎng)絡(luò)請(qǐng)求框架的封裝,網(wǎng)絡(luò)請(qǐng)求的工作本質(zhì)上是 OkHttp 完成,而 Retrofit 僅負(fù)責(zé)網(wǎng)絡(luò)請(qǐng)求接口的封裝,其內(nèi)部實(shí)現(xiàn)實(shí)際上是使用了代理模式,為了更好的學(xué)習(xí)Retrofit框架,我們先從代理模式開始。
代理模式
代理模式,就是為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。如果在直接訪問(wèn)對(duì)象時(shí)帶來(lái)的問(wèn)題,比如說(shuō):要訪問(wèn)的對(duì)象在遠(yuǎn)程的機(jī)器上。在面向?qū)ο笙到y(tǒng)中,有些對(duì)象由于某些原因(比如對(duì)象創(chuàng)建開銷很大,或者某些操作需要安全控制,或者需要進(jìn)程外的訪問(wèn)),直接訪問(wèn)會(huì)給使用者或者系統(tǒng)結(jié)構(gòu)帶來(lái)很多麻煩,我們可以在訪問(wèn)此對(duì)象時(shí)加上一個(gè)對(duì)此對(duì)象的訪問(wèn)層。
代理模式的使用
來(lái)看一個(gè)代理模式的例子。
定義接口:
/**
* 代理抽象角色: 定義了服務(wù)的接口
* 代理角色和真實(shí)角色對(duì)外提供的公共方法,一般為一個(gè)接口
*/
public interface Sing{
public void sing();
}
實(shí)現(xiàn)接口:
/**
* 實(shí)現(xiàn)類: 提供唱歌服務(wù)的Jack
* 定義了真是角色所要實(shí)現(xiàn)的業(yè)務(wù)邏輯
*/
public class Jack implements Sing{
@Override
public void sing(){
System.out.println("努力唱歌")
}
}
代理對(duì)象:
/**
* 代理對(duì)象:演唱代理人
* 是真實(shí)角色的代理,通過(guò)真實(shí)角色的業(yè)務(wù)邏輯方法來(lái)實(shí)現(xiàn)抽象方法,并可以附加自己的操作
* 將統(tǒng)一的流程控制都放到代理角色中處理。
*/
public class SingAgent implements Sing {
private final Sing sing;
public Agent(Sing Sing) {
this.Sing = sing;
}
//....前置處理
public void before() {
System.out.println("準(zhǔn)備");
}
//....后置處理
public void after() {
System.out.println("打分");
}
@Override
public void massage() {
before();
Sing.sing();
after();
}
}
代理模式使用:
public class Main {
public static void main(String[] args) throws Exception {
//靜態(tài)代理
Sing sing = new Jack();
SingAgent agent = new Agent(sing);
agent.sing();
}
}
在靜態(tài)代理中,一個(gè)代理類可以代理多個(gè)真實(shí)對(duì)象,我們除了定義Jack之外,還可以定義David。如果我們的Jack除了會(huì)Sing(唱歌)之外,還會(huì)Dance(跳舞)。
public interface Dance{
public void dance();
}
public class Jack implements Sing, Dance{
......
}
但是一個(gè)代理,只能實(shí)現(xiàn)一個(gè)抽象接口,為了能代理Jack對(duì)象,必須再創(chuàng)建一個(gè)DanceAgent,這樣一來(lái),代碼中就會(huì)有很多代理類。所以我們就必須想辦法,通過(guò)一個(gè)代理類,實(shí)現(xiàn)全部的代理功能,這時(shí)候就用到了動(dòng)態(tài)代理。
public static void main(String[] args) throws Exception {
//靜態(tài)代理
Jack jack = new Jack();
Object o = Proxy.newProxyInstance(Main.class.getClassLoader(), //類加載器
new Class[]{ Sing.class, Dance.class }, //代理的接口數(shù)組
new InvocationHandler() { //回調(diào)方法
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
// System.out.println(o.toString());
return method.invoke(jack, objects);
}
});
Sing sing = (Sing) o;
sing.sing();
}
在這里,使用了Proxy類中的newProxyInstance方法,需要提供三個(gè)參數(shù),類加載器、代理的接口數(shù)組、回調(diào)方法。先創(chuàng)建了一個(gè)Jack對(duì)象,然后創(chuàng)建了一個(gè)動(dòng)態(tài)代理對(duì)象o,然后通過(guò)類型轉(zhuǎn)換,將o轉(zhuǎn)化成要代理的接口,調(diào)用sing()方法的時(shí)候,就會(huì)觸發(fā)InvocationHandler.invoke()這個(gè)回調(diào)。回調(diào)方法中也有三個(gè)參數(shù),o就是我們的動(dòng)態(tài)對(duì)理對(duì)象,method就是接口的方法,objects就是方法參數(shù)。然后調(diào)用method.invoke()真正實(shí)現(xiàn)jack的sing()方法
所以,現(xiàn)在我們大概能了解到什么時(shí)候使用代理模式了吧。比如在早期網(wǎng)絡(luò)實(shí)現(xiàn)的時(shí)候,用的是Volley框架,如果對(duì)于每一條網(wǎng)絡(luò)請(qǐng)求,都是直接用Volley去建立,那么到了后面換成Okhttp框架的時(shí)候,就要對(duì)每一條網(wǎng)絡(luò)請(qǐng)求都去修改代碼,十分麻煩。而使用代理模式,則可以添加一個(gè)服務(wù)接口(定義了get和post),把Volley封裝,并創(chuàng)建代理類,在代理對(duì)象中調(diào)用封裝層的真實(shí)實(shí)現(xiàn)。后期切換框架的時(shí)候也更方便。
Proxy.newProxyInstance()原理
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone(); //拷貝接口
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs); //獲取Class對(duì)象
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams); //利用反射獲取class 的構(gòu)造方法
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h}); //對(duì)class進(jìn)行實(shí)例化
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
在newProxyInstance方法中,interfaces.clone()先對(duì)接口數(shù)組進(jìn)行了拷貝,然后利用getProxyClass0方法創(chuàng)建Class對(duì)象,然后通過(guò)反射,獲得它的構(gòu)造方法cons,最后利用cons.newInstance()創(chuàng)建該類的實(shí)例。
我們的類是怎么來(lái)的?我們先來(lái)回顧一下類的完整生命周期。
Java源文件(.java) —編譯—> Java字節(jié)碼(.class) —類加載—> Class對(duì)象 —實(shí)例化—> 實(shí)例對(duì)象 ———>卸載
而我們的.class文件一般都是一個(gè)實(shí)實(shí)在在的文件,是在硬盤中存在的。但是在動(dòng)態(tài)代理中,他這個(gè)Class對(duì)象,是在內(nèi)存中去生成的。我們來(lái)看getProxyClass0這個(gè)方法
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
return proxyClassCache.get(loader, interfaces);
}
在Proxy里面有一個(gè)proxyClassCache對(duì)象,該對(duì)象是一個(gè)WeakCache實(shí)例,該緩存可以保存已經(jīng)生成過(guò)的代理類,如果有則直接返回。如果沒有的話,則通過(guò)ProxyClassFactory去創(chuàng)建代理類對(duì)象。
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
......
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
......
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
ProxyClassFactory中,通過(guò)ProxyGenerator.generateProxyClass創(chuàng)建了代理類數(shù)據(jù),返回的是一個(gè)byte數(shù)組,然后通過(guò)defineClass0去解析這個(gè)byte數(shù)組,并生成一個(gè)代理類對(duì)象。我們可以把這個(gè)byte數(shù)組通過(guò)數(shù)據(jù)流的方式輸出到文件中。
private static void proxy() throws Exception {
String name = Sing.class.getName() + "$Proxy0";
//生成代理指定接口的Class數(shù)據(jù)
byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Sing.class});
FileOutputStream fos = new FileOutputStream("lib/" + name + ".class");
fos.write(bytes);
fos.close();
}
我們來(lái)觀察它的文件結(jié)構(gòu)。
public final class Sing$Proxy0 extends Proxy implements Sing {
private static Method m3;
//構(gòu)造方法接受InvocationHandler
public Sing$Proxy0(InvocationHandler var1) throws {
super(var1);
}
......
//在super中,也就是Proxy中,h就是傳入的InvocationHandler
public final void sing() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
......
//靜態(tài)代碼塊中,利用反射,獲得sing()方法
static {
try {
m3 = Class.forName("com.enjoy.lib.Sing").getMethod("sing");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
Retrofit的簡(jiǎn)單實(shí)現(xiàn)
在接下來(lái),我們將利用注解、反射與動(dòng)態(tài)代理,對(duì)Retrofit進(jìn)行一個(gè)簡(jiǎn)單的實(shí)現(xiàn)。在此之前,我們先來(lái)看一下Retrofit的簡(jiǎn)單使用。
Retrofit的基本使用
Api定義:
創(chuàng)建接api接口
//這里使用的是高德地圖提供的天氣api
public interface WeatherApi {
@POST("/v3/weather/weatherInfo")
@FormUrlEncoded
Call<ResponseBody> postWeather(@Field("city") String city, @Field("key") String key);
@GET("/v3/weather/weatherInfo")
Call<ResponseBody> getWeather(@Query("city") String city, @Query("key") String key);
}
Retrofit使用:
創(chuàng)建Retrofit對(duì)象,然后利用create()創(chuàng)建了api接口的對(duì)象,最后調(diào)用api的方法。
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://restapi.amap.com")
.build();
weatherApi = retrofit.create(WeatherApi.class);
Call<ResponseBody> call = weatherApi.getWeather("110101", "ae6c53e2186f33bbf240a12d80672d1b");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()){
ResponseBody body = response.body();
try {
String string = body.string();
Log.i(TAG, "onResponse get: " + string);
} catch (IOException e) {
e.printStackTrace();
} finally {
body.close();
}
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
觀察上面代碼,Retrofit在創(chuàng)建的時(shí)候,使用的是構(gòu)建者模式,它可以將一個(gè)復(fù)雜對(duì)象的構(gòu)建和它的表示分離,可以讓使用者方便使用,不必知道內(nèi)部的細(xì)節(jié)。而在創(chuàng)建Api接口對(duì)象的時(shí)候, 使用的就是動(dòng)態(tài)代理。接下來(lái)我們來(lái)實(shí)現(xiàn)一個(gè)自己的Retrofit。
EnjoyRetrofit.java
public class EnjoyRetrofit {
final Map<Method, ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();
final Call.Factory callFactory;
final HttpUrl baseUrl;
EnjoyRetrofit(Call.Factory callFactory, HttpUrl baseUrl) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
}
public <T> T create(final 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 {
//解析這個(gè)method 上所有的注解信息
ServiceMethod serviceMethod = loadServiceMethod(method);
//args:
return serviceMethod.invoke(args);
}
});
}
private ServiceMethod loadServiceMethod(Method method) {
//先不上鎖,避免synchronized的性能損失
ServiceMethod result = serviceMethodCache.get(method);
if (result != null) return result;
//多線程下,避免重復(fù)解析,
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
/**
* 構(gòu)建者模式,將一個(gè)復(fù)雜對(duì)象的構(gòu)建和它的表示分離,可以使使用者不必知道內(nèi)部組成的細(xì)節(jié)。
*/
public static final class Builder {
private HttpUrl baseUrl;
//Okhttp->OkhttClient
private okhttp3.Call.Factory callFactory; //null
public Builder callFactory(okhttp3.Call.Factory factory) {
this.callFactory = factory;
return this;
}
public Builder baseUrl(String baseUrl) {
this.baseUrl = HttpUrl.get(baseUrl);
return this;
}
public EnjoyRetrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
return new EnjoyRetrofit(callFactory, baseUrl);
}
}
}
ServiceMethod.java
/**
* 記錄請(qǐng)求類型 參數(shù) 完整地址
*/
public class ServiceMethod {
private final Call.Factory callFactory;
private final String relativeUrl;
private final boolean hasBody;
private final ParameterHandler[] parameterHandler;
private FormBody.Builder formBuild;
HttpUrl baseUrl;
String httpMethod;
HttpUrl.Builder urlBuilder;
public ServiceMethod(Builder builder) {
baseUrl = builder.enjoyRetrofit.baseUrl;
callFactory = builder.enjoyRetrofit.callFactory;
httpMethod = builder.httpMethod;
relativeUrl = builder.relativeUrl;
hasBody = builder.hasBody;
parameterHandler = builder.parameterHandler;
//如果是有請(qǐng)求體,創(chuàng)建一個(gè)okhttp的請(qǐng)求體對(duì)象
if (hasBody) {
formBuild = new FormBody.Builder();
}
}
public Object invoke(Object[] args) {
/**
* 1 處理請(qǐng)求的地址與參數(shù)
*/
for (int i = 0; i < parameterHandler.length; i++) {
ParameterHandler handlers = parameterHandler[i];
//handler內(nèi)本來(lái)就記錄了key,現(xiàn)在給到對(duì)應(yīng)的value
handlers.apply(this, args[i].toString());
}
//獲取最終請(qǐng)求地址
HttpUrl url;
if (urlBuilder == null) {
urlBuilder = baseUrl.newBuilder(relativeUrl);
}
url = urlBuilder.build();
//請(qǐng)求體
FormBody formBody = null;
if (formBuild != null) {
formBody = formBuild.build();
}
Request request = new Request.Builder().url(url).method(httpMethod, formBody).build();
return callFactory.newCall(request);
}
// get請(qǐng)求, 把 k-v 拼到url里面
public void addQueryParameter(String key, String value) {
if (urlBuilder == null) {
urlBuilder = baseUrl.newBuilder(relativeUrl);
}
urlBuilder.addQueryParameter(key, value);
}
//Post 把k-v 放到 請(qǐng)求體中
public void addFiledParameter(String key, String value) {
formBuild.add(key, value);
}
public static class Builder {
private final EnjoyRetrofit enjoyRetrofit;
private final Annotation[] methodAnnotations;
private final Annotation[][] parameterAnnotations;
ParameterHandler[] parameterHandler;
private String httpMethod;
private String relativeUrl;
private boolean hasBody;
public Builder(EnjoyRetrofit enjoyRetrofit, Method method) {
this.enjoyRetrofit = enjoyRetrofit;
//獲取方法上的所有的注解
methodAnnotations = method.getAnnotations();
//獲得方法參數(shù)的所有的注解 (一個(gè)參數(shù)可以有多個(gè)注解,一個(gè)方法又會(huì)有多個(gè)參數(shù))
parameterAnnotations = method.getParameterAnnotations();
}
public ServiceMethod build() {
/**
* 1 解析方法上的注解, 只處理POST與GET
*/
for (Annotation methodAnnotation : methodAnnotations) {
if (methodAnnotation instanceof POST) {
//記錄當(dāng)前請(qǐng)求方式
this.httpMethod = "POST";
//記錄請(qǐng)求url的path
this.relativeUrl = ((POST) methodAnnotation).value();
// 是否有請(qǐng)求體
this.hasBody = true;
} else if (methodAnnotation instanceof GET) {
this.httpMethod = "GET";
this.relativeUrl = ((GET) methodAnnotation).value();
this.hasBody = false;
}
}
/**
* 2 解析方法參數(shù)的注解
*/
int length = parameterAnnotations.length;
parameterHandler = new ParameterHandler[length];
for (int i = 0; i < length; i++) {
// 一個(gè)參數(shù)上的所有的注解
Annotation[] annotations = parameterAnnotations[i];
// 處理參數(shù)上的每一個(gè)注解
for (Annotation annotation : annotations) {
//todo 可以加一個(gè)判斷:如果httpMethod是get請(qǐng)求,現(xiàn)在又解析到Filed注解,可以提示使用者使用Query注解
if (annotation instanceof Field) {
//得到注解上的value: 請(qǐng)求參數(shù)的key
String value = ((Field) annotation).value();
parameterHandler[i] = new ParameterHandler.FiledParameterHandler(value);
} else if (annotation instanceof Query) {
String value = ((Query) annotation).value();
parameterHandler[i] = new ParameterHandler.QueryParameterHandler(value);
}
}
}
return new ServiceMethod(this);
}
}
}
ParameterHandler.java
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;
}
//serviceMethod: 回調(diào)
@Override
void apply(ServiceMethod serviceMethod, String value) {
serviceMethod.addQueryParameter(key,value);
}
}
static class FiledParameterHandler extends ParameterHandler {
String key;
public FiledParameterHandler(String key) {
this.key = key;
}
@Override
void apply(ServiceMethod serviceMethod, String value) {
serviceMethod.addFiledParameter(key,value);
}
}
}
定義注解:
Field.java
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Field {
String value();
}
GET.java
@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {
String value() default "";
}
POST.java
@Target(METHOD)
@Retention(RUNTIME)
public @interface POST {
String value() default "";
}
Query.java
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Query {
String value();
}
最后,我們來(lái)看如何使用自定義的Retrofit。
public class MainActivity extends AppCompatActivity {
private WeatherApi weatherApi;
private static final String TAG = "MainActivity";
private EnjoyWeatherApi enjoyWeatherApi;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EnjoyRetrofit enjoyRetrofit = new EnjoyRetrofit.Builder().baseUrl("https://restapi.amap.com").build();
enjoyWeatherApi = enjoyRetrofit.create(EnjoyWeatherApi.class);
}
public void enjoyGet(View view) {
okhttp3.Call call = enjoyWeatherApi.getWeather("110101", "ae6c53e2186f33bbf240a12d80672d1b");
call.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
}
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
Log.i(TAG, "onResponse enjoy get: " + response.body().string());
response.close();
}
});
}
public void enjoyPost(View view) {
okhttp3.Call call = enjoyWeatherApi.postWeather("110101", "ae6c53e2186f33bbf240a12d80672d1b");
call.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
}
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
Log.i(TAG, "onResponse enjoy post: " + response.body().string());
response.close();
}
});
}
}