Retrofit教程之【源码解析】篇

前言

如果还不了解Retrofit的,建议先看看我之前文章,起码对Retrofit的使用有一个大概的了解。

传送门:

  1. Retrofit教程之【新手入门】篇
  2. Retrofit教程之【深入源码】篇 ⬅️当前位置
  3. Retrofit教程之【设计模式】篇

这里会围绕Retrofit的请求过程来分析源码,涉及的代码以及文字可能会比较长,建议先阅读Retrofit教程之【新手入门】篇 再来阅读这篇文章会更加容易。

另外完整的代码我已经上传到了github,有需要可以去下载

网络请求过程

使用Retrofit完成一次网络请求过程,大概分为几个步骤:


图1

首先看创建Retrofit的代码

 Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

这里是采用了一个经典的Facade(门面模式)+Builder(创建者模式)来设计Retrofit,目的就是将Retrofit内部复杂的创建过程给屏蔽,并且能够简单的构建。

然后调用Retrofit.create方法来创建具体的接口

ApiService service = retrofit.create(service);

我们定位到Retrofit.create代码里看一下:

public final class Retrofit {
  public <T> T create(final Class<T> service) {
    // 校验声明的接口及参数是否合法
    // 这里需要注意的是,你的接口不能是Void的或者其他类型,否则会报错
    validateServiceInterface(service);
    // 动态代理创建出具体的Service
    return (T)
            Proxy.newProxyInstance(
                    service.getClassLoader(),
                    new Class<?>[] {service},
                    new InvocationHandler() {
                        // 获取当前系统平台信息,具体区别是有无java8
                        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 (method.getDeclaringClass() == Object.class) {
                                //这里回调的就是在接口中声明的具体的参数
                                return method.invoke(this, args);
                            }
                            args = args != null ? args : emptyArgs;
                            // 如果是默认的,则调用默认的方式只在API26+有效
                            // 否则 调用loadServiceMethod
                            return platform.isDefaultMethod(method)
                                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                                    : loadServiceMethod(method).invoke(args);
                        }
                    });
}

           ...省略代码...
}

这里面需要注意的是validateServiceInterface方法,这里面会根据validateEagerlyboolean变量对接口进行提前校验,代码如下:

if (validateEagerly) {
  Platform platform = Platform.get();
  for (Method method : service.getDeclaredMethods()) {
    if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
      loadServiceMethod(method);
    }
  }
}

然后再回到loadServiceMethod()方法:

 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;
  }

这里我们注意到,serviceMethodCache这个Map会缓存住已经解析过注解的ServiceMethod对象。这里其实是用了一个非常经典的享元模式来处理这种有很多对象创建的场景,好处就是大大减少对象的创建,降低系统的内存,使效率提高。

如果从serviceMethodCache获取不到对应的value,则使用ServiceMethod进行注解的解析。注解的解析最后是交给了RequestFactory来处理

RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

解析注解

然后点开RequestFactory源码,可以发现,所有你在接口中加上的注解,都会在这里被解析成对应的网络请求的参数,这里就不放所有代码了。

RequestFactory build() {
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(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);
  }
}

完成上面的解析,才会返回一个完整的我们声明的API接口实现类的实例,最后返回Call对象,发起真正的网络请求。
好,我们回过头来看一下,整个的流程:


image.png

找到“真凶”

接下来就是真正的网络请求了

 Call<Result<NewsResult>> call = service.getNewsByPost(map);
 call.enqueue(new Callback<Result<NewsResult>>() {
        @Override
        public void onResponse(Call<Result<NewsResult>> call, Response<Result<NewsResult>> response) {
            updateResult(response);
        }

        @Override
        public void onFailure(Call<Result<NewsResult>> call, Throwable t) {

        }
    });

我们看下Call对象的源码,可以得出Call对象是负责发起请求,取消请求的接口协议,那么具体的调用,肯定是在具体的实现类里。

public interface Call<T> extends Cloneable {
   /**
    * Asynchronously send the request and notify {@code callback} of its response or if an error
    * occurred talking to the server, creating the request, or processing the response.
    *
    * 异步发送请求,如果和服务器发起会话,创建请求,或者处理Response都会通知到callback
    */
    void enqueue(Callback<T> callback);

    void cancel();
   
     Response<T> execute() throws IOException;
 }

点击Call接口,发现有2个类实现了它

  • ExecutorCallbackCall
  • OkHttpCall

然后通过debug发现最终是走向了ExecutorCallbackCall中的实现,看代码:

   static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    @Override
    public void enqueue(final Callback<T> callback) {
        Objects.requireNonNull(callback, "callback == null");

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

                    @Override
                    public void onFailure(Call<T> call, final Throwable t) {
                        callbackExecutor.execute(() -> callback.onFailure(DefaultCallAdapterFactory.ExecutorCallbackCall.this, t));
                    }
                });
    }

我们注意到equeue()方法里的实现,又交给了一个Call类型的delegate对象,

那么你会好奇,明明已经实现了,为什么又委托给另一个实现类?
而且已知的实现类就2个,难道另一个是OkHttpCall?

没错,这个Call的实现类就是OkHttpCall,这其实是因为在一开始创建Retrofit对象的时候,也就是在build的时候就已经初始化了一个默认的CallAdapterFactory,代码如下:

 // Make a defensive copy of the adapters and add the default Call adapter.
  List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
  callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

那它又是在什么时候初始化的呢?

这里调用的路径比较长,我们只要知道最终是在HttpServiceMethodparseAnnotations()方法里。

知道这个delegate对象是由OkHttpCall,接下来我们转到equeue方法这里:

 public void enqueue(final Callback<T> callback) {
    okhttp3.Call call;
    
    // 此处代码略
    synchronized (this) {
        if (call == null && failure == null) {
            try {
                // 创建原始的Call对象
                call = rawCall = createRawCall();
            } catch (Throwable t) {
            }
        }
    }
    // 此处代码略
    call.enqueue(
            new okhttp3.Callback() {
                @Override
                public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
                    Response<T> response;
                    try {
                        // 解析原始的Response
                        response = parseResponse(rawResponse);
                    } catch (Throwable e) {
                        return;
                    }

                }
                @Override
                public void onFailure(okhttp3.Call call, IOException e) {
                    callFailure(e);
                }

            });
}

通过代码可以发现,这里又创建了一个Call对象,当然不是上文的Call,而是okhttp3.Call对象,这里通过创建指向了RealCall对象,也就是交给它来处理加入的请求。

Dispatcher分发

接下来就是重头戏了,

final class RealCall implements Call {
 final OkHttpClient client;
 @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
      transmitter.callStart();
      // 划重点!!!!
      client.dispatcher().enqueue(new AsyncCall(responseCallback));
   }
}

一下子出来了3个新鲜玩意:

  1. OkHttpClient(这个应该不陌生)
  2. dispatcher。
  3. AsyncCall。

咱们一一来看,第一个不说了,大家都应该知道。
第二个,点开Dispatcher源码,看注释,

Policy on when async requests are executed.Each dispatcher uses an ExecutorService to run calls internally. If you supply your own executor, it should be able to run the configured maximum number of calls concurrently.

翻译过来就是个自定义线程池,如果你想玩,你也可以自己搞一个。
再看源码:

  public final class Dispatcher {
    private int maxRequests = 64;// 最大请求数64
    private int maxRequestsPerHost = 5;
    private @Nullable Runnable idleCallback;

    /** Executes calls. Created lazily. */
    private @Nullable
    ExecutorService executorService;

    /** Ready async calls in the order they'll be run. */
    private final Deque<RealCall.AsyncCall> readyAsyncCalls = new ArrayDeque<>();

    /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
    private final Deque<RealCall.AsyncCall> runningAsyncCalls = new ArrayDeque<>();

    /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

我们可以总结出,这个线程池有几个特点:

  1. 最多能处理64个请求;
  2. 每个主机最多能处理5个请求;
  3. 可以自定义线程池,但是必须能够定义的最大请求数maxRequests;
  4. 维持着3种队列,来处理不同状态的任务;

然后是AsyncCall,这其实是RealCall的一个内部类,等会再说。
再回到一开始加入AsynCall的代码:

client.dispatcher().enqueue(new AsyncCall(responseCallback));

也就是new了一个AsyncCall对象交给Dispatcher线程池类进行分发执行了。那么在执行的时候,通过代码可以发现:

void enqueue(RealCall.AsyncCall call) {
    synchronized (this) {
        // 添加到待执行的队列中
        readyAsyncCalls.add(call);
           // 。。。此处代码略。。。
    // 执行
    promoteAndExecute();
}

private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<RealCall.AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
        
        // 1. 遍历readyAsyncCalls
        for (Iterator<RealCall.AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
            RealCall.AsyncCall asyncCall = i.next();
            
            if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
            if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.

            i.remove();
            asyncCall.callsPerHost().incrementAndGet();
            // 2. 过滤出可用的添加到executableCalls队列中
            executableCalls.add(asyncCall);
            runningAsyncCalls.add(asyncCall);
        }
        isRunning = runningCallsCount() > 0;
    }
    // 3. 遍历executableCalls,并执行
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
        RealCall.AsyncCall asyncCall = executableCalls.get(i);
        asyncCall.executeOn(executorService());
    }

    return isRunning;
}

最后是调用到了AsyncCall里的execute()方法,查看源码

 @Override protected void execute() {
    // 此处代码略   
    try {
        Response response = getResponseWithInterceptorChain();
        signalledCallback = true;
        responseCallback.onResponse(RealCall.this, response);
    } catch (IOException e) {
       
    }
    // 此处代码略
}

终于看到和返回结果相关的Response对象了,这一路走来我容易吗我5555!!
但是接下来我发现,离终点还远着呢。

击鼓传花之Interceptor

接下来也是Retrofit比较重要的部分,那就是:Interceptor
下面我们把重点放到getResponseWithInterceptorChain方法上。

往往一个方法名起的够长,我就知道事情没那么简单!

ok,看源码!

 Response getResponseWithInterceptorChain() throws IOException {
    List<Interceptor> interceptors = new ArrayList<>();

    // 1. 添加Interceptor
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    interceptors.add(new CallServerInterceptor(forWebSocket));

    // 2. 交给Interceptor.Chain,
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
            originalRequest, this, client.connectTimeoutMillis(),
            client.readTimeoutMillis(), client.writeTimeoutMillis());

    //3. 递归调用
    Response response = chain.proceed(originalRequest);

}

由于代码过长,省略了部分代码。

一下子,又多了这么多类,这都是干嘛的?
这个方法又是什么意思?

首先,看我在代码里的注释,1.2.3明白了吗,不明白再看一遍!
这里的Interceptor采用了JAVA设计模式中又很经典的一个责任链模式(Chain of Responsibility),如果不理解这个设计模式的话,那这段代码理解起来会有些难度,不过你可以通俗的理解为“击鼓传花”,大概也就明白了。

不过这里可以理解为 “拦截链”,只是个名字,千万不要把他认为是责任链模式的名字。按照代码中所添加的拦截器,总结如下:


不过需要注意的是,最后的网络真正的请求是在最后一个CallServerInterceptor中完成返回Response对象的。

完美转换Converter

拿到Response对象还没完,因为返回的数据都是原始数据,没有经过处理。由于我们之前添加了GsonConverterFactory,所以我们的数据还要经过Gson转换器来进行数据转换。

 Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

转换实在OkHttpCall中的parseResponse方法中实现的,看源码:

retrofit2.Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    OkHttpCall.ExceptionCatchingResponseBody catchingBody = new OkHttpCall.ExceptionCatchingResponseBody(rawBody);
    try {
        // 数据转换
        T body = responseConverter.convert(catchingBody);
        return retrofit2.Response.success(body, rawResponse);
    } catch (RuntimeException e) {
        // If the underlying source threw an exception, propagate that rather than indicating it was
        // a runtime exception.
        catchingBody.throwIfCaught();
        throw e;
    }
}

那么这个responseConverter对象又是在什么时候初始化的呢?

通过源码可以定位实在Retrofit通过动态代理反射创建接口时的InvocationHandlerinvoke()方法里。
最后,在通过在回调处理我们的数据。

整体的流程,我用processOn工具整理出来个流程图:


流程图

总结

  1. 实际网络请求过程并没有没有深入分析,因为实际的网络请求更复杂,这里只是结合着Retrofit的源码来进行分析。
  2. 看源码的过程,不仅是一个学习的过程,更要思考作者是如何设计的。
  3. 设计模式在这里运用的只能说是一个字:秒!其实,仔细想想,责任链模式不是早应该运用在这里了么?!!
  4. 注解式的框架有很多种,Retrofit的注解算是运用的恰到好处!
  5. 当然最牛逼的还是他的解耦方式,把请求过程每一个步骤所涉及的类都充分解耦,这种设计思想没有多年的架构实战经验是设计不出来的。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容