当前位置: 代码迷 >> 综合 >> Retrofit2.0源码完全解析
  详细解决方案

Retrofit2.0源码完全解析

热度:88   发布时间:2024-01-18 04:49:23.0

1 Retrofit 和 Okhttp 的关系

现在android端主流的网络框架就是 retrofit + okhttp,那么,它们之间的关系到底是什么呢,其实,真正做网络请求的是okhttp,retrofit只是对它的进一步封装。既然okhttp已经能够实现网络相关操作了,为什么还需要retrofit呢?

很简单,用一个词就能够解释,adapter,adapter的意思是适配器,可以实现各种的转化。比如,你想返回值是一个User类型,但是默认返回值是ResponsBody。这个时候你如果不添加任何的Converter,是会报错的。所以,我们经常需要添加这么一段代码 

addConverterFactory(GsonConverterFactory.create())

这段代码的意思是添加一个转化器,使得我们能够直接使用Gson对Json进行编码和解码。这样,我们就能使用自己需要的类型作为返回值。

更重要的是,它能够和Rxjava完美配合,实现Rxjava的异步功能。需要添加下面的代码

addCallAdapterFactory(RxJava2CallAdapterFactory.create())

这样,你不再是使用okhttp里面的Call和Callback,而是Observal和Observe。为什么转化成Rxjava这么重要呢,因为Rxjava能够很好的实现线程的调度。比如,网络操作是需要在子线程中操作的,而Retrofit的所有操作默认都是在主线程中实现的。然后,返回的结果又需要在主线程中显示。这个是最简单的。只需要切换两次,那复杂一点的呢,比如,网络结果需要先经过一些耗时操作,然后再把操作结果显示到界面上,这理又需要进行一次线程的切换,而使用Rxjava就能够优雅的实现,无论有多复杂的线程切换。

综上所诉,Okhttp是核心,实现相关网络操作。Retrofit是一层包在Okhttp外面的皮,它本身写起来很简单,用注解写一些接口和参数类型就可以,它配合ConverterFactory使解析更容易,它配合CallAdapterFactory使网络操作在线程间切换更方便。

2  Retrofit使用的设计模式—动态代理

面试的时候经常会问到Retrofit相关知识,其中一个必问的就是,Retrofit用的是什么设计模式。动态代理,大部分人基本都能答出来,接着会问,什么是动态代理。其实大家都有一些理解,也知道大概是怎么回事。但是却无法表达出来。那么,到底什么是动态代理呢。

GitHubService service = retrofit.create(GitHubService.class);

点金create查看创建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.adapt(okHttpCall);}});

这就是动态代理的实现,通过调用Proxy.newProxyInstance()。这个方法需要传三个参数,第一个是ClassLoader,也就是类加载器,第二个是传入一个Class,需要代理的类,这里把GithubService这个接受传进去,第三个最复杂,是InvocationHandler,需要重写它的invoke方法。那么,它具体做了什么事呢。其实很简单,比如,我们的GithubService是这样的。

@GET("users/123/repos")
Call<List<Repo>> listRepos();@GET("users/456/repos")Call<List<Repo>> listRepos2();

实际上,它会创建GithubService的实例,这这个实例实现了接口中的所有方法。同时,它会创建一个InvocationHandler对象,实现里面的invoke()方法,具体实现是在源码中。然后每一个实现的接口中,比如listRepos()和listRepos2()的具体实现,都会调用InvocationHandler的invoke()方法。当然,中间还需要运用反射来获取方法,因为这些都是在运行中才实现的。运行中获取方法,变量等,都需要用到反射。

3 Retrofit的源码解析

上文中,我们已经知道了动态代理的具体实现,很明显,最关键的代码就是InvocationHandler的invoke()的代码了,因为这里面的代码会被每一个具体的接口调用。接下来,我们看一下InvocationHandler的invoke()方法。

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.adapt(okHttpCall);}});

前面是两个if判断,第一个if上面有个注释,如果方法是来自Object的方法,则正常调用。意思就是,如果这个方法不是声明在GithubService里面的,那么直接调用这个方法本身,就是做一个简单的判断而已。第二个判断,是判断这个方法是不是默认的方法,因为我们的接口都是不实现,所以也不会走这个方法。所以,最主要的就是下面几行的代码。

ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);

查看loadServiceMethod(method)的源码,我们会发现里面用到建造者模式,查看它的源码,我们会发现,里面除了做一些判断外,最主要的是parseMethodAnnotation(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);if (!Void.class.equals(responseType)) {throw methodError("HEAD method must use Void as response type.");}
} 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);
}

看到这里,应该恍然大悟,原来这个方法就是对注解进行判断,我们平时写起来那么轻松,用@GET,@POST就可以设置请求方法,那具体是怎么操作的呢,都在这个方法里面。而且逻辑也非常简单,就是用if来判断。另外还有一个重要的方法是parseParameterAnnotation,从名字我们能够大概猜到这个方法的意思,解析参数的注解。果不其然,是对参数进行解析,最常用的就i是@Path,@Body这些。再大概看一下其它的方法,就能够看出loadServiceMethod这个方法的主要作用,就是负责获取我们创建的interface中的所有信息,通过一系列的判断解析这些信息。

下一行是创建okHttpCall的对象,我们看一下这个方法的内部具体做了什么。首先okHttpCall是实现了retrofit的Call接口的一个对象,而里面是先创建一个okhtt3的call,然后对它进行封装。它把上文解析的serviceMethod对象封装进okHttpCall里面,把里面包含的信息创建一个okhttp3的call,并用这个okhttp3的call发起网络请求,然后对结果进行一些处理。

@Override public synchronized Request request() {okhttp3.Call call = rawCall;if (call != null) {return call.request();}if (creationFailure != null) {if (creationFailure instanceof IOException) {throw new RuntimeException("Unable to create request.", creationFailure);} else if (creationFailure instanceof RuntimeException) {throw (RuntimeException) creationFailure;} else {throw (Error) creationFailure;}}try {return (rawCall = createRawCall()).request();} catch (RuntimeException | Error e) {throwIfFatal(e); // Do not assign a fatal error to creationFailure.creationFailure = e;throw e;} catch (IOException e) {creationFailure = e;throw new RuntimeException("Unable to create request.", e);}
}

最后一行代码是serviceMethod.adapt(okHttpCall),调用的是adapt方法,而adapt一般是和适配器相关的,应该是一些转化。点进去看一下,发现它调用的是 callAdapter.adapt(call)方法,那么,我们先看一下 callAdapter是什么东西。点击它的源码,最终我们看到如下代码

@Override public void enqueue(final Callback<T> callback) {checkNotNull(callback, "callback == null");delegate.enqueue(new Callback<T>() {@Override public void onResponse(Call<T> call, final Response<T> response) {callbackExecutor.execute(new Runnable() {@Override public void run() {if (delegate.isCanceled()) {// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));} else {callback.onResponse(ExecutorCallbackCall.this, response);}}});}@Override public void onFailure(Call<T> call, final Throwable t) {callbackExecutor.execute(new Runnable() {@Override public void run() {callback.onFailure(ExecutorCallbackCall.this, t);}});}});
}

从这个方法中可以看出,是把callBack的onResponse和onFailure交给callbackExecutor.execute来实现,而这个很明显是进行线程的切换的,把后台线程发起的网络请求在主线程中调用返回结果,实现线程的自动切换。最终我们得出结论,这个adapt方法是和线程切换有关的。当然,它不止把返回结果切换到主线程这么简单,还能和RxJava完美适配。

上述所说的,就是retrofit的主体结构,是由三部分构成,每部分都有自己对应的功能。第一部分是读取接口中的相关信息,第二部分是把相关信息封装进自己的call对象,然后在这个call对象中调用okhttp3的call方法进行网络请求。第三部分是最复杂的,是进行一个切换,也可以说是一个适配,默认帮我们实现的有,把网络请求的结果回调在主线程中,还能完美适配RxJava,使用RxJava的相关功能。