Retrofit
是 square
公司的另外一款普遍流行的网络请求框架。前面的一篇文章《源码分析OKHttp执行过程》已经对 OkHttp
网络请求框架有一个大概的了解。今天一样地对 Retrofit
的源码进行走读,对其底层的实现逻辑作到心中有数。java
Retrofit
的项目地址为:https://github.com/square/retrofitgit
打开项目目录下的 samples
文件夹,从这里能够浏览 Retrofit
项目的使用范例。github
在本文中打开SimpleService.java
这个类做为源码走读的入口。这个类很简单,展现了 Retrofit
的基本用法json
public final class SimpleService { //定义接口请求地址 public static final String API_URL = "https://api.github.com"; //定义接口返回数据的实体类 public static class Contributor { public final String login; public final int contributions; public Contributor(String login, int contributions) { this.login = login; this.contributions = contributions; } } //定义网络请求接口 public interface GitHub { //这个是请求github项目代码贡献者列表的接口 //使用@GET注解指定GET请求,并指定接口请求路径,使用大括号{}定义的参数,是形参,retrofit会把方法中的 //@Path 传入到请求路径中 @GET("/repos/{owner}/{repo}/contributors") Call<List<Contributor>> contributors( @Path("owner") String owner, @Path("repo") String repo); } public static void main(String... args) throws IOException { // 建立一个retrofit,而且指定了接口的baseUrl // 而后设置了一个gson转换器,用于将接口请求下来的json字符串转换为Contributor实体类。 Retrofit retrofit = new Retrofit.Builder() .baseUrl(API_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); // 这里是魔法所在,retrofit将程序猿定义的接口变成“实现类” GitHub github = retrofit.create(GitHub.class); //经过retrofit这个“实现类”执行contributors方法 Call<List<Contributor>> call = github.contributors("square", "retrofit"); // 执行Call类中的execute方法,这是一个同步方法 // 固然跟okhttp同样,异步方法是enqueue,这个下文会提到 List<Contributor> contributors = call.execute().body(); for (Contributor contributor : contributors) { System.out.println(contributor.login + " (" + contributor.contributions + ")"); } } }
经过上面代码的阅读,知道 retrofit
使用流程api
API
retrofit
对象,指定baseUrl
和数据转换器(即接口数据解析器,如对json
、xml
、protobuf
等数据类型的解析)retrofit
将程序猿定义的 API
接口变成"实现类"这个流程关键点是四、五、6,下文将详细对这几个步骤的源码进行阅读。数组
在继续下文以前,咱们先看看这个SimpleService
的执行结果,它打印了retrofit
这个项目的代码贡献者缓存
JakeWharton (928) swankjesse (240) pforhan (48) eburke (36) dnkoutso (26) NightlyNexus (26) edenman (24) loganj (17) Noel-96 (16) rcdickerson (14) rjrjr (13) kryali (9) adriancole (9) holmes (7) swanson (7) JayNewstrom (6) crazybob (6) Jawnnypoo (6) danrice-square (5) vanniktech (5) Turbo87 (5) naturalwarren (5) guptasourabh04 (4) artem-zinnatullin (3) codebutler (3) icastell (3) jjNford (3) f2prateek (3) PromanSEW (3) koalahamlet (3)
从上文的源码阅读中,能够看出程序猿只是定义了一个接口,可是如今实现接口的工做是由 retrofit
来实现的网络
GitHub github = retrofit.create(GitHub.class); Call<List<Contributor>> call = github.contributors("square", "retrofit");
打开 retrofit.create
方法框架
public <T> T create(final Class<T> service) { //对接口进行校验 Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } //经过Proxy建立了一个代理 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 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); } //判断是否为默认方法,Java8中接口也能够有默认方法,因此这里有这个判断 if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } //关键点 return loadServiceMethod(method).invoke(args != null ? args : emptyArgs); } }); }
这个方法很短,关键是经过 Proxy
建立了一个 Github
接口的代理类并返回该代理。koa
newProxyInstance
方法须要3个参数:ClassLoader
、Class<?>
数组、InvocationHandler
回调。
这个 InvocationHandler
很是关键,当执行接口 Github
的contributors
方法时,会委托给InvocationHandler
的invoke
方法来执行。即Github
将接口代理给了Proxy
来执行了。
接着看InvocationHandler
接口的实现。
在 invoke
方法中有三个参数,其中proxy
就是代理对象,而 method
就是程序猿定义的那个网络请求接口,顾名思义 args
就是方法的参数。
此方法最终是调用了
loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
打开 loadServiceMethod
方法
ServiceMethod<?> loadServiceMethod(Method method) { // 判断是否有缓存 ServiceMethod<?> result = serviceMethodCache.get(method); if (result != null) return result; //同步处理 synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { //没有获取到缓存则使用`ServiceMethod`方法来建立 result = ServiceMethod.parseAnnotations(this, method); //最后缓存起来 serviceMethodCache.put(method, result); } } return result; }
这个方法就是经过 method
来获取一个 ServiceMethod
对象。
打开 ServiceMethod
发现它是一个抽象类,有一个静态方法 parseAnnotations
和一个抽象方法 invoke
。
abstract class ServiceMethod<T> { static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) { //对注解进行解析 RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); //获取方法的返回类型 Type returnType = method.getGenericReturnType(); //对返回类型进行校验 if (Utils.hasUnresolvableType(returnType)) { throw methodError(method, "Method return type must not include a type variable or wildcard: %s", returnType); } if (returnType == void.class) { throw methodError(method, "Service methods cannot return void."); } //最终使用到HttpServiceMethod类 return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); } abstract T invoke(Object[] args); }
parseAnnotations
方法就是对程序猿定义的接口中使用的注解进行解析。
最后是使用了HttpServiceMethod.parseAnnotations
方法
/** Adapts an invocation of an interface method into an HTTP call. */ final class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> { static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) { CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method); //...省略部分代码 Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType); okhttp3.Call.Factory callFactory = retrofit.callFactory; return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter); } //...省略部分代码 @Override ReturnT invoke(Object[] args) { return callAdapter.adapt( new OkHttpCall<>(requestFactory, args, callFactory, responseConverter)); } }
HttpServiceMethod
是 ServiceMethod
的子类。而在parseAnnotations
方法中构造了HttpServiceMethod
实例并返回。
所以,loadServiceMethod
方法返回的是HttpServiceMehod
对象
这样下面代码的执行其实是执行了 HttpServiceMehod
的 invoke
方法。
loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
再次翻看上文中HttpServiceMethod
类
@Override ReturnT invoke(Object[] args) { return callAdapter.adapt( new OkHttpCall<>(requestFactory, args, callFactory, responseConverter)); }
invoke
方法里有执行了callAdapter.adapt
方法,参数为OkHttpCall
,这个类实际上就是对okhttp
网络请求的封装,这里也能够看出retrofit
内部是使用了okhttp
来执行网络请求的
public interface CallAdapter<R, T> { //..省略部分代码 T adapt(Call<R> call); //CallAdapter抽象工厂类 abstract class Factory { //返回CallAdapter实例 public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit); //..省略部分代码 } }
这是一个接口,内部有一个Factory
抽象工厂类,用于获取CallAdapter
对象。
CallAdapter
有不少子类,那 callAdapter.adapt
方法执行的是哪一个具体类的方法呢?实际上,从调试代码中能够发现是调用DefaultCallFactory
中的内部实现类
final class DefaultCallAdapterFactory extends CallAdapter.Factory { static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory(); @Override public @Nullable CallAdapter<?, ?> get( Type returnType, Annotation[] annotations, Retrofit retrofit) { if (getRawType(returnType) != Call.class) { return null; } final Type responseType = Utils.getCallResponseType(returnType); //返回一个CallAapter实例 return new CallAdapter<Object, Call<?>>() { @Override public Type responseType() { return responseType; } @Override public Call<Object> adapt(Call<Object> call) { //将参数返回,而这个参数就是OKHttpCall的实例 return call; } }; } }
能够发现,在adapt
方法中就是将参数call
返回。
因此下面代码返回的是OkHttpCall
对象。
loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
综上
//建立了Github接口的代理类 GitHub github = retrofit.create(GitHub.class); //执行接口的方法,其实就是调用了代理类的方法,并最终返回了一个OKhttpCall对象 //而这个对象就是对Okhttp的封装 Call<List<Contributor>> call = github.contributors("square", "retrofit");
上文中获取到OKhttpCall
对象,它只是把接口请求过程进行了封装,并无真正的获取到接口数据。要获取到接口数据还须要调用OkHttpCall.execute
方法
List<Contributor> contributors = call.execute().body();
这里的请求过程与前文中《源码分析OKHttp执行过程》介绍的是相似的。接一下
打开OkHttpCall.execute
方法
@Override public Response<T> execute() throws IOException { okhttp3.Call call; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; if (creationFailure != null) { if (creationFailure instanceof IOException) { throw (IOException) creationFailure; } else if (creationFailure instanceof RuntimeException) { throw (RuntimeException) creationFailure; } else { throw (Error) creationFailure; } } call = rawCall; if (call == null) { try { call = rawCall = createRawCall(); } catch (IOException | RuntimeException | Error e) { throwIfFatal(e); // Do not assign a fatal error to creationFailure. creationFailure = e; throw e; } } } if (canceled) { call.cancel(); } return parseResponse(call.execute()); }
这里的执行逻辑也很简单
synchronized
进行同步操做createRawCall
建立 okhttp3.Call
对象okhttp
的Call.execute
方法,并解析response
后返回请求结果一样地,异步请求操做也是相似的
打开OkHttpCall.enqueue
方法
@Override public void enqueue(final Callback<T> callback) { checkNotNull(callback, "callback == null"); okhttp3.Call call; Throwable failure; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; call = rawCall; failure = creationFailure; if (call == null && failure == null) { try { //建立okhttp网络请求 call = rawCall = createRawCall(); } catch (Throwable t) { throwIfFatal(t); failure = creationFailure = t; } } } if (failure != null) { callback.onFailure(this, failure); return; } if (canceled) { call.cancel(); } //最终是执行了OkHttp中的call.enqueue方法 //并回调相应的接口 call.enqueue(new okhttp3.Callback() { @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) { Response<T> response; try { response = parseResponse(rawResponse); } catch (Throwable e) { throwIfFatal(e); callFailure(e); return; } try { callback.onResponse(OkHttpCall.this, response); } catch (Throwable t) { t.printStackTrace(); } } @Override public void onFailure(okhttp3.Call call, IOException e) { callFailure(e); } private void callFailure(Throwable e) { try { callback.onFailure(OkHttpCall.this, e); } catch (Throwable t) { t.printStackTrace(); } } }); }
这个方法其实最终都是执行了okhttp
的相应方法。
Retrofit
其实一种更加高级的网络应用框架,经过代理模式简化了接口的定义,无需提供接口的具体实现就能够完成网络接口请求的执行。它的底层其实是封装了 okhttp
的执行过程,也把对网络的操做进行了封装,而对于程序猿来讲只须要关注业务逻辑,对网络请求的具体实现没必要关心。
例如在本文开头的实例中咱们只须要定义接口,定义实体类,其余工做都交给了 Retrofit
,接下来就是Magic
。