博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Retrofit源码分析三 源码分析
阅读量:6367 次
发布时间:2019-06-23

本文共 13313 字,大约阅读时间需要 44 分钟。

Retrofit源码分析三 源码分析

使用方法

我们先来看一下Retrofit的常见使用方法:

//创建网络请求接口类public interface GitHubService {  @GET("users/{user}/repos")  Call
> listRepos(@Path("user") String user);}//创建Retrofit实例对象Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build();//通过动态代理创建网络接口代理对象GitHubService service = retrofit.create(GitHubService.class);//获取Call对象Call
> repos = service.listRepos("octocat");//执行同步请求或异步请求repos.execute();repos.enqueue(callback)复制代码

上面是Retrofit的最基本使用方法,当然现在使用最多的还是RxJava2+Retrofit搭配使用,关于RxJava2,大家可以看我的另一篇 ,当然RxJava2与Retrofit搭配使用的解析我会在稍后分析,这里我们先关注最基本的使用方法。

创建网络接口类

这一步的目的就是封装我们网络请求相关的一些参数,没什么好多说的。

创建Retrofit实例对象

Retrofit实例对象的创建很明显是采用了Builder模式,Builder模式在 Java语言中 创建一个 有很多可选配置参数 的对象的时候是很好的一种设计模式。Builder模式有两个重点,一个是在 Java语言中 中,另一个是 有很多可选配置参数,其实在现在的Android开发中,使用Kotlin开发Android已经很普遍了,熟悉Kotlin语法的小伙伴可能很熟悉了,由于Kotlin中 默认参数 的存在,所以在Kotlin中使用Builder模式的意义不大。但由于Java语法的限制,在创建一个 有很多可选配置参数 的时候,Builder模式还是首要选择。

我们来看一下Retrofit中的成员变量:

public final class Retrofit {  //缓存封装好的ServiceMethod  private final Map
> serviceMethodCache = new ConcurrentHashMap<>(); //OKHttp中的网络请求工厂 final okhttp3.Call.Factory callFactory; //BaseUrl final HttpUrl baseUrl; //数据转换器工厂集合 final List
converterFactories; //网络请求适配器工厂集合 final List
callAdapterFactories; //处理线程切换 final @Nullable Executor callbackExecutor; //不需要关注,默认为false final boolean validateEagerly; //忽略无关代码...... }复制代码

serviceMethodCache 是一个HashMap,它的Key是Method,代表我们定义的网络请求接口类中的方法,它的Value是ServiceMethod,代表对网络请求接口类中的方法的一个封装,简单看一下它就明白了:

final class ServiceMethod
{ private final okhttp3.Call.Factory callFactory; private final CallAdapter
callAdapter; private final HttpUrl baseUrl; private final Converter
responseConverter; private final String httpMethod; private final String relativeUrl; private final Headers headers; private final MediaType contentType; private final boolean hasBody; private final boolean isFormEncoded; private final boolean isMultipart; private final ParameterHandler
[] parameterHandlers; //忽略无关代码....... }复制代码

很明显了,ServiceMethod就是对网络请求参数的封装类,包含请求头、相对url、GET请求或者是POST请求等等对请求的配置信息。serviceMethodCache 就是一个对ServiceMethod的缓存,可以提高一定的效率。

在Retrofit中,默认的数据转换器工厂就是 GsonConverterFactory ,所以其实如果我们是使用 Gson 来做数据转换的,其实没有必要去配置。在Android平台,Retrofit中默认的网络请求适配器工厂是 ExecutorCallAdapterFactory ,我们简单瞅一眼它是如何被创建的:

static class Android extends Platform {    @Override public Executor defaultCallbackExecutor() {      return new MainThreadExecutor();    }    @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {      if (callbackExecutor == null) throw new AssertionError();      return new ExecutorCallAdapterFactory(callbackExecutor);    }    static class MainThreadExecutor implements Executor {      //注意这里创建了一个位于主线程的Handler      private final Handler handler = new Handler(Looper.getMainLooper());      @Override public void execute(Runnable r) {        //通过handler.post(runnable)实现线程切换        handler.post(r);      }    }  }复制代码

可以看到,在创建 ExecutorCallAdapterFactory 的同时传入了一个 callbackExecutor ,这个 callbackExecutor 也是Retrofit中默认的 callbackExecutor ,在Android平台中它是 MainThreadExecutor类型 ,可以看到,在它的内部创建了一个位于主线程的Handler。我们知道,使用Retrofit的时候不同于直接使用OKHttp,在使用Retrofit的异步网络请求时,网络结果的回调是位于主线程中的,那么Retrofit是如何切换的线程,看过上面的代码应该会心里有个数了,它是通过 handler.post(r) 来实现由子线程到主线程的切换。

通过动态代理创建网络接口代理对象

看过我的Retrofit源码分析第二篇:代理模式的小伙伴们应该都比较清楚了,在Retrofit的 create 方法中其实是使用了动态代理生成了一个代理对象,现在我们就来看一下 create 的源码:

public 
T create(final Class
service) { //忽略无关代码...... //下面就是JDK给我们提供的动态代理了 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 { //忽略无关代码...... //获取method的网络请求参数的封装类ServiceMethod对象 ServiceMethod
serviceMethod = (ServiceMethod
) loadServiceMethod(method); //获取对OKHttp中的RealCall的一个包装类OkHttpCall对象 OkHttpCall
okHttpCall = new OkHttpCall<>(serviceMethod, args); //通过网络请求适配器将原始的Call对象转换成需要的对象,比如RxJavaCallAdapter会将Call对象转换成Observable。 return serviceMethod.adapt(okHttpCall); } }); }复制代码

create 方法中做了3件事:

  • 封装网络请求方法
  • 封装原始Call对象
  • 通过CallAdapter转换Call对象 封装网络请求方法很容易理解,关于ServiceMethod上面已经说过,不再赘述。封装原始Call对象,对OKHttp源码熟悉的小伙伴应该知道,原始的Call其实就是OkHttp中的ReallCall。如果不熟悉OkHttp的同学可以看我之前的一篇 。我们可以简单看一下这个包装类 OkHttpCall :
final class OkHttpCall
implements Call
{ //忽略无关代码...... @Override public Response
execute() throws IOException { okhttp3.Call call; //熟悉OkHttp源码的同学应该很熟悉了,完全照抄OkHttp中的代码,确保每个Call只会被执行一次 synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; //忽略无关代码...... call = rawCall; if (call == null) { try { //创建OkHttp中的RealCall对象 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(); } //解析OkHttp中的RealCall执行同步方法后返回的网络数据 return parseResponse(call.execute()); } @Override public void enqueue(final Callback
callback) { checkNotNull(callback, "callback == null"); okhttp3.Call call; Throwable failure; //确保一个Call对象只被执行一次 synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; call = rawCall; failure = creationFailure; if (call == null && failure == null) { try { //创建OkHttp中的RealCall对象 call = rawCall = createRawCall(); } catch (Throwable t) { throwIfFatal(t); failure = creationFailure = t; } } } if (failure != null) { callback.onFailure(this, failure); return; } if (canceled) { call.cancel(); } //调用OkHttp中的RealCall的异步请求网络方法 call.enqueue(new okhttp3.Callback() { @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) { Response
response; try { response = parseResponse(rawResponse); } catch (Throwable 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(); } } }); }}复制代码

可以看到,Retrofit中的这个 OKHttpCall 完全是对OkHttp中的 RealCall 的一个包装。在 OKHttpCall 中的同步网络方法 execute 和异步网络方法 enQueue 中其实就做了三件事:获取 RealCall 对象,调用 RealCall 中的 execute 或者 enQueue 方法,然后去解析OkHttp返回的原始数据并转换成我们需要的类型,就这么简单。

接下来就 create 方法中就只剩下 通过CallAdapter转换Call对象 了,我们上面说过,在没有特别配置CallAdapter的时候,默认的CallAdapterFactory是 ExecutorCallAdapterFactory ,很明显,CallAdapter是由Factory创建的,那我们看一下这个默认的CallAdapterFactory:

final class ExecutorCallAdapterFactory extends CallAdapter.Factory {      //本质是传入的MainThreadExecutor  final Executor callbackExecutor;  ExecutorCallAdapterFactory(Executor callbackExecutor) {    this.callbackExecutor = callbackExecutor;  }  @Override  public CallAdapter
get(Type returnType, Annotation[] annotations, Retrofit retrofit) { if (getRawType(returnType) != Call.class) { return null; } final Type responseType = Utils.getCallResponseType(returnType); //直接创建并返回一个CallAdapter的匿名对象 return new CallAdapter
>() { @Override public Type responseType() { return responseType; } @Override public Call
adapt(Call call) { //返回默认的CallAdapter转换后的Call对象 return new ExecutorCallbackCall<>(callbackExecutor, call); } }; } static final class ExecutorCallbackCall
implements Call
{ //本质是传入的MainThreadExecutor final Executor callbackExecutor; //就是OKHttpCall,从名字也能理解,代理Call对象 final Call
delegate; ExecutorCallbackCall(Executor callbackExecutor, Call
delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } @Override public void enqueue(final Callback
callback) { checkNotNull(callback, "callback == null"); //调用OkHttp中RealCall的异步网络请求 delegate.enqueue(new Callback
() { @Override public void onResponse(Call
call, final Response
response) { //注意,OkHttp的异步回调是在子线程的,Retrofit这里实现了线程的切换,本质是调用了handler.post(runnable) callbackExecutor.execute(new Runnable() { @Override public void run() { if (delegate.isCanceled()) { callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled")); } else { callback.onResponse(ExecutorCallbackCall.this, response); } } }); } @Override public void onFailure(Call
call, final Throwable t) { //注意,OkHttp的异步回调是在子线程的,Retrofit这里实现了线程的切换,本质是调用了handler.post(runnable) callbackExecutor.execute(new Runnable() { @Override public void run() { callback.onFailure(ExecutorCallbackCall.this, t); } }); } }); } @Override public boolean isExecuted() { return delegate.isExecuted(); } @Override public Response
execute() throws IOException { //由于同步方法不需要切线程,所以直接执行并返回OKHttpCall的同步网络方法 return delegate.execute(); } @Override public void cancel() { delegate.cancel(); } @Override public boolean isCanceled() { return delegate.isCanceled(); } @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone. @Override public Call
clone() { return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone()); } @Override public Request request() { return delegate.request(); } }}复制代码

上面的代码其实也很清晰了 ExecutorCallAdapterFactory 这个CallAdapter工厂类直接创建并返回一个CallAdapter的匿名对象,这个其实就是我们Retrofit的默认CallAdapter,关键是这个CallAdapter的adapt方法,它返回了 ExecutorCallbackCall 这个对OkHttpCall的包装类,我们关注这个包装类的 enqueue 方法,在这个方法中,它通过调用 callbackExecutor.execute 来实现了子线程到主线程的线程切换。这个 callbackExecutor 就是 MainThreadExecutor ,这个类我们上面提到过, MainThreadExecutor 中的 execute 方法内部就是 handler.post(r); ,这个 handler 其实就是主线程的,因此实现了线程切换。

获取Call对象并执行同步或异步方法

其实经过上一步的分析,我们已经知道了,我们获取的Call对象,其实是通过动态代理中 serviceMethod.adapt(okHttpCall) 返回的Call对象,这个其实就是 ExecutorCallbackCall ,这个我们都很清楚了,它是对OkHttp中RealCall的一个包装类,在它的异步方法中通过调用 callbackExecutor.execute 实现了线程的切换,当然本质还是通过 Handler 机制。

其实到此为止,基本的流程已经分析完毕了,我们到这里也会很清楚Retrofit的定位:Retrofit并不是一个网络请求的框架,而是一封装网络请求参数,解析网络返回结果的框架。下面我们来分析一下使用了RxJava2CallAdapter的情况。

RxJava2CallAdapter的原理

CallAdapter的重点是它的adapt方法,我们来看一下RxJava2CallAdapter中的adapt方法:

@Override public 
Object adapt(Call
call) { //创建一个发射网络返回结果的Observable Observable
> responseObservable = new CallObservable<>(call); Observable
observable; if (isResult) { observable = new ResultObservable<>(responseObservable); } else if (isBody) { observable = new BodyObservable<>(responseObservable); } else { observable = responseObservable; } //默认scheduler为空 if (scheduler != null) { observable = observable.subscribeOn(scheduler); } if (isFlowable) { return observable.toFlowable(BackpressureStrategy.LATEST); } if (isSingle) { return observable.singleOrError(); } if (isMaybe) { return observable.singleElement(); } if (isCompletable) { return observable.ignoreElements(); } return observable; }复制代码

这段代码本质是创建一个发射网络返回结果的Observable,我们看一下 CallObservable 的内部实现,看过RxJava2源码的同学应该都知道,Observable的关键方法是 subscribeActual,我们就看一下这个方法内部都做了什么:

@Override protected void subscribeActual(Observer
> observer) { // Since Call is a one-shot type, clone it for each new observer. Call
call = originalCall.clone(); observer.onSubscribe(new CallDisposable(call)); boolean terminated = false; try { //调用了OKHttpCall的同步网络访问方法,并获取网络数据 Response
response = call.execute(); if (!call.isCanceled()) { //将获取到的网络数据发送到观察者observer observer.onNext(response); } if (!call.isCanceled()) { terminated = true; //发送结束事件 observer.onComplete(); } } catch (Throwable t) { Exceptions.throwIfFatal(t); if (terminated) { RxJavaPlugins.onError(t); } else if (!call.isCanceled()) { try { //发送错误事件 observer.onError(t); } catch (Throwable inner) { Exceptions.throwIfFatal(inner); RxJavaPlugins.onError(new CompositeException(t, inner)); } } } }复制代码

熟悉RxJava2的同学看完就应该懂了:RxJava2CallAdapter通过adapt方法生成并返回一个 CallObservable 对象,在这个 CallObservable 内部通调用 OKHttpCallexecute() 方法进行网络访问,并将获取到的数据发送到下一级的观察者observer中。

到此为止,Retrofit的源码分析终于结束了,其实它并不难,但想要完全理解整个网络访问流程,除了要明白Retrofit,还需要了解OKHttp甚至是RxJava,对后两者不太熟悉的小伙伴们可以看我的另外两篇源码分析: , ,在熟悉了OKHttp和RxJava2的基础上再回过头来看Retrofit,相信你会有更深的理解。

转载地址:http://dcrma.baihongyu.com/

你可能感兴趣的文章
Node.js有了新的管理者
查看>>
Java 20年:历史与未来
查看>>
彻底理解Javascript中的原型链与继承
查看>>
腾讯最大规模裁撤中层干部,让贤年轻人
查看>>
gRPC-Web发布,REST又要被干掉了?
查看>>
如何:强化 TCP/IP 堆栈安全
查看>>
Spring3 MVC中使用Swagger生成API文档
查看>>
FastCGI PHP on Windows Server 2003
查看>>
LimeSDR Getting Started Quickly | LimeSDR上手指南
查看>>
JSP标签JSTL的使用(1)--表达式操作
查看>>
SAP顾问的人脉比技术更为重要
查看>>
FI/CO PA考试试卷
查看>>
汽车介质应用非常严苛?没关系,新技术带来的高精度传感器十分适应!
查看>>
天合光能 - 用计算捕捉“光的能量”
查看>>
使用sysbench压力测试MySQL(一)(r11笔记第3天)
查看>>
css知多少(11)——position
查看>>
【Spring】定时任务详解实例-@Scheduled
查看>>
先有的资源,能看的速度看,不能看的,抽时间看。说不定那天就真的打不开了(转)...
查看>>
[20161028]rman与filesperset=1.txt
查看>>
哪些领域适合开发微信小程序
查看>>