Retrofit2源码解析——网络调用流程(下)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介:

Retrofit2源码解析系列

本文基于Retrofit2的2.4.0版本

implementation 'com.squareup.retrofit2:retrofit:2.4.0'

上次我们分析到网络请求是通过OkHttpCall类来完成的,下面我们就来分析下OkHttpCall类。

final class OkHttpCall<T> implements Call<T> {

    ...
    @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 {
                    //调用createRawCall创建OkHttp3的Call
                    call = rawCall = createRawCall();
                } catch (Throwable t) {
                    throwIfFatal(t);
                    failure = creationFailure = t;
                }
            }
        }

        ...

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

OkHttpCall的enqueue方法主要干了2件事,一个是创建OkHttp3的Call用于执行网络请求;另一个是解析返回的结果并回调。下面我们来看看创建OkHttp3的Call的过程

//OkHttpCall.class
private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = serviceMethod.toCall(args);
    if (call == null) {
        throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
}

可以发现是通过serviceMethod的toCall方法来创建的

//ServiceMethod.class
okhttp3.Call toCall(@Nullable Object... args) throws IOException {
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
            contentType, hasBody, isFormEncoded, isMultipart);

    ...
    for (int p = 0; p < argumentCount; p++) {
        handlers[p].apply(requestBuilder, args[p]);
    }
    //最后调用OkHttpClient的newCall方法返回Call
    return callFactory.newCall(requestBuilder.build());
}

ServiceMethod的toCall方法也是通过OkHttpClient的newCall方法来返回Call的。

在我们通过OkHttpClient请求得到结果后,我们还需要将返回的结果Response解析成我们接口需要的实体类型,这就需要用到我们在创建Retrofit时设置的ConverterFactory了,比如GsonConverterFactory。

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

    rawResponse = rawResponse.newBuilder()
            .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
            .build();

    ...

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
        //通过serviceMethod的toResponse方法解析
        T body = serviceMethod.toResponse(catchingBody);
        return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
        
        catchingBody.throwIfCaught();
        throw e;
    }
}

OkHttpCall的parseResponse方法调用的是serviceMethod的toResponse方法来解析返回的结果。

//ServiceMethod.class
R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
}

在ServiceMethod中最后调用responseConverter的convert方法来转换返回的结果。这个responseConverter和上面分析的CallAdapter的确定过程一样,也是在ServiceMethod的build方法中,通过调用retrofit的requestBodyConverter方法遍历我们传入的ConverterFactory,直到找到合适的。

//Retrofit.class
public <T> Converter<T, RequestBody> requestBodyConverter(Type type,
                                                              Annotation[] parameterAnnotations, Annotation[] methodAnnotations) {
    return nextRequestBodyConverter(null, type, parameterAnnotations, methodAnnotations);
}

public <T> Converter<T, RequestBody> nextRequestBodyConverter(
        @Nullable Converter.Factory skipPast, Type type, Annotation[] parameterAnnotations,
        Annotation[] methodAnnotations) {
    ...

    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
        Converter.Factory factory = converterFactories.get(i);
        Converter<?, RequestBody> converter =
                factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this);
        if (converter != null) {
            //noinspection unchecked
            return (Converter<T, RequestBody>) converter;
        }
    }

    ...
}

需要注意的是在创建Retrofit时默认添加了一个BuiltInConverters,这个是Retrofit为我们提供一个默认的responseConverter,它主要处理的是返回类型是ResponseBody和Void的情况。

final class BuiltInConverters extends Converter.Factory {
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
                                                            Retrofit retrofit) {
        if (type == ResponseBody.class) {
            return Utils.isAnnotationPresent(annotations, Streaming.class)
                    ? StreamingResponseBodyConverter.INSTANCE
                    : BufferingResponseBodyConverter.INSTANCE;
        }
        if (type == Void.class) {
            return VoidResponseBodyConverter.INSTANCE;
        }
        return null;
    }
    ...
}

因为我们一般返回值类型都是具体的实体类型,所以我们需要添加自己的responseConverter,一般也就是GsonConverterFactory了。

至此,网络调用的后半部分流程也清楚了:

我们调用Call对象的enqueue方法发起异步请求时,实际上调用的是OkHttpCall对应的enqueue方法。OkHttpCall会先调用ServiceMethod类的toCall方法利用OkHttpClient的newCall方法创建OkHttp3的call对象,然后利用这个call对象执行具体的网络请求。在网络请求返回成功以后会调用ServiceMethod类的toResponse方法利用我们设置的responseConverter将返回结果转换成我们需要的类型,然后通过我们设置的回调或是默认的回调方法,将结果回调回主线程,从而完成整个请求过程。

总结

Retrofit2的网络调用的整个流程我们已经分析完了。通过这次分析,我们可以看到Retrofit2中最主要的就是3个类:Retrofit、ServiceMethod和OkHttpCall。这三个类指责明确,相互配合共同完成整个网络调用的流程。

(1)Retrofit负责供外部初始化和定制,保存CallAdapter的列表和ResponseConverterFactory列表。

(2)ServiceMethod对应每一个接口方法的信息,包括解析注解和参数等,同时它也是连接Retrofit和OkHttpCall的桥梁。ServiceMethod中保存着当前接口对应方法所需要的CallAdapter和ResponseConverter。利用CallAdapter将OkHttpCall转换成接口需要的类型,供接口调用。利用toResponse方法让OkHttpCall调用ResponseConverter解析网络请求返回的结果。

(3)OkHttpCall则是用来执行具体网络请求。Retrofit2没有直接使用OkHttp3的Call接口,而是有自己的Call接口。在OkHttpCall内部通过组合的方法持有OkHttp3的Call接口,并通过ServiceMethod的toCall方法得到OkHttp3的call来进行网络请求,减少对OkHttp3的耦合。


欢迎关注我的微信公众号,和我一起每天进步一点点!
AntDream

目录
相关文章
|
5天前
|
安全 虚拟化
在数字化时代,网络项目的重要性日益凸显。本文从前期准备、方案内容和注意事项三个方面,详细解析了如何撰写一个优质高效的网络项目实施方案,帮助企业和用户实现更好的体验和竞争力
在数字化时代,网络项目的重要性日益凸显。本文从前期准备、方案内容和注意事项三个方面,详细解析了如何撰写一个优质高效的网络项目实施方案,帮助企业和用户实现更好的体验和竞争力。通过具体案例,展示了方案的制定和实施过程,强调了目标明确、技术先进、计划周密、风险可控和预算合理的重要性。
20 5
|
5天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
16 2
|
7天前
|
SQL 安全 网络安全
网络安全的护城河:漏洞防御与加密技术的深度解析
【10月更文挑战第37天】在数字时代的浪潮中,网络安全成为守护个人隐私与企业资产的坚固堡垒。本文将深入探讨网络安全的两大核心要素——安全漏洞和加密技术,以及如何通过提升安全意识来强化这道防线。文章旨在揭示网络攻防战的复杂性,并引导读者构建更为稳固的安全体系。
18 1
|
16天前
|
SQL 安全 测试技术
网络安全的盾牌与剑——漏洞防御与加密技术解析
【10月更文挑战第28天】 在数字时代的浪潮中,网络空间安全成为我们不可忽视的战场。本文将深入探讨网络安全的核心问题,包括常见的网络安全漏洞、先进的加密技术以及提升个人和组织的安全意识。通过实际案例分析和代码示例,我们将揭示黑客如何利用漏洞进行攻击,展示如何使用加密技术保护数据,并强调培养网络安全意识的重要性。让我们一同揭开网络安全的神秘面纱,为打造更加坚固的数字防线做好准备。
35 3
RS-485网络中的标准端接与交流电端接应用解析
RS-485,作为一种广泛应用的差分信号传输标准,因其传输距离远、抗干扰能力强、支持多点通讯等优点,在工业自动化、智能建筑、交通运输等领域得到了广泛应用。在构建RS-485网络时,端接技术扮演着至关重要的角色,它直接影响到网络的信号完整性、稳定性和通信质量。
|
5天前
|
网络协议 网络安全 网络虚拟化
本文介绍了十个重要的网络技术术语,包括IP地址、子网掩码、域名系统(DNS)、防火墙、虚拟专用网络(VPN)、路由器、交换机、超文本传输协议(HTTP)、传输控制协议/网际协议(TCP/IP)和云计算
本文介绍了十个重要的网络技术术语,包括IP地址、子网掩码、域名系统(DNS)、防火墙、虚拟专用网络(VPN)、路由器、交换机、超文本传输协议(HTTP)、传输控制协议/网际协议(TCP/IP)和云计算。通过这些术语的详细解释,帮助读者更好地理解和应用网络技术,应对数字化时代的挑战和机遇。
30 3
|
5天前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
18 2
|
5天前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
13天前
|
SQL 安全 算法
网络安全的屏障与钥匙:漏洞防护与加密技术解析
【10月更文挑战第31天】在数字世界的海洋中,网络安全是航船的坚固屏障,而信息安全则是守护宝藏的金钥匙。本文将深入探讨网络安全的薄弱环节——漏洞,以及如何通过加密技术加固这道屏障。从常见网络漏洞的类型到最新的加密算法,我们不仅提供理论知识,还将分享实用的安全实践技巧,帮助读者构建起一道更加坚不可摧的防线。
23 1
|
9天前
|
机器学习/深度学习 人工智能 自动驾驶
深入解析深度学习中的卷积神经网络(CNN)
深入解析深度学习中的卷积神经网络(CNN)
26 0

推荐镜像

更多