OkHttp源码剖析(一) 初识okhttp

OkHttp源码剖析(一) 初识okhttp

OkHttp源码剖析(二) 设计模式下的okhttp

OkHttp源码剖析(三) 任务调度器Dispatcher

OkHttp源码剖析(四) 报文读写工具ExchangeCodec

OkHttp源码剖析(五) 代理路由

使用示例

本篇主要基于okhttp-4.9.0进行分析。下面是OkHttp主要的使用示例。

			  OkHttpClient client = new OkHttpClient.Builder().build();
        Request request = new Request
                .Builder()
                .url("https://www.yanfriends.com")
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.e("okhttp response", response.body().string() );
            }
        });

架构图

OkHttp的流程图大致如下图所示,这里先有个大致感观,每一环的细节都会在接下来的章节中说到,再看完所有章节后回过头来再看下,会对OkHttp的整个流程架构有清晰的认识。

本质理解

http的连接本质上是个socket,根据http协议,通过socket包装发送请求并获得返回结果。

网路连接库一开始的样子如下代码所示,其实只要符合Http协议的请求,就可以和网络进行交互,类似于OkHttp的网络请求库,帮助开发者方便和屏蔽了Http协议中类似于请求头,重连、合并、代理、返回结果解析等等Http协议细节的应用层实现。

				val path = "http://www.baidu.com/"
        val host = "www.baidu.com"
        var socket: Socket? = null
        var streamWriter: OutputStreamWriter? = null
        var bufferedWriter: BufferedWriter? = null
        try {
            socket = Socket(host, 80)
            streamWriter = OutputStreamWriter(socket.getOutputStream())
            bufferedWriter = BufferedWriter(streamWriter)
            bufferedWriter.write("GET $path HTTP/1.1\r\n")
            bufferedWriter.write("Host: www.baidu.com\r\n")
            bufferedWriter.write("\r\n")
            bufferedWriter.flush()
            val myRequest = BufferedReader(InputStreamReader(socket.getInputStream(), "UTF-8"))
            var d = -1
            while (myRequest.read().also({ d = it }) != -1) {
                print(d.toChar())
            }
        } catch (e: IOException) {
            e.printStackTrace()
        }

OkHttpClient

OkHttpClient是整个OkHttp的配置中心。

所有的call请求都会共享OkHttpClient中的配置,如出错重试、连接池、日志、各种拦截器等。

OkHttpClient实现看Call.Factory接口,是Call的工厂类,生产 Call并通过 Call 来发起 HTTP Request 获取 Response。

OkHttpClient应该被共享,使用时保持单例,因为每个Client 都会有一个自己的连接池和线程池,复用 Client 可以减少资源的浪费。

OkHttpClient 中的配置主要有:

  • Dispatcher dispatcher :用于调度后台发起的网络请求的调度器, 有后台总请求数和单主机总请求数的控制。

  • List<Protocol> protocols :支持的应用层协议,即 HTTP/1.1、 HTTP/2 等。

  • List<ConnectionSpec> connectionSpecs :应用层支持的 Socket 设置,即使用明文传输(用于 HTTP)还是某个版本的 TLS(用于 HTTPS)。

  • List<Interceptor> interceptors :大多数时候使用的 Interceptor 都应该配置到这里。

  • List<Interceptor> networkInterceptors :直接和网络请求交互 的 Interceptor 配置到这里,例如如果你想查看返回的 301 报文或者未解压 的 Response Body,需要在这里看。

  • CookieJar cookieJar :管理 Cookie 的控制器。OkHttp 提供了 Cookie 存取的判断支持(即什么时候需要存 Cookie,什么时候需要读取 Cookie,但没有给出具体的存取实现。如果需要存取 Cookie,你得自己写实现,例如用 Map 存在内存里,或者用别的方式存在本地存储或者数据库。

  • Cache cache :Cache 存储的配置。默认是没有,如果需要用,得自己配置出 Cache 存储的文件位置以及存储空间上限。

  • HostnameVerifier hostnameVerifier :用于验证 HTTPS 握手过程 中下载到的证书所属者是否和自己要访问的主机名一致。

  • CertificatePinner certificatePinner :用于设置 HTTPS 握手 过程中针对某个 Host 额外的的 Certificate Public Key Pinner,即把网站证 书链中的每一个证书公钥直接拿来提前配置进 OkHttpClient 里去,作为正 常的证书验证机制之外的一次额外验证。

  • Authenticator authenticator :用于自动重新认证。配置之后,在 请求收到 401 状态码的响应是,会直接调用 authenticator ,手动加 入 Authorization header 之后自动重新发起请求。

  • boolean followRedirects :是否允许重定向

  • boolean followSslRedirects :不是是否自动 follow HTTPS URL,是在重定向时发生了协议切换 ,是否允许,默认 true。

  • 重定向的意思,而是是否自动 follow 在 HTTP 和 HTTPS 之间切换的重定向。

  • boolean retryOnConnectionFailure :在请求失败的时候是否自动 重试。注意,大多数的请求失败并不属于 OkHttp 所定义的「需要重试」, 这种重试只适用于「同一个域名的多个 IP 切换重试」「Socket 失效重试」 等情况。

  • int connectTimeout :建立连接(TCP 或 TLS)的超时时间,默认十秒;
    int readTimeout :发起请求到读到响应数据的超时时间,默认十秒;
    int writeTimeout :发起请求并被目标服务器接受的超时时间。(因为有时候对方服务器可能由于某种原因而不读取你的 Request),默认十秒;

粗识okhttp

OkHttp中的细节很多,这里不过分深入,只需有个大致的结构认识即可,一些细节会在接下来的文章中具体分析。

RealCall初始化

使用OkHttpClient.newCall()创建RealCall和Transmitter类,Transmitter类可以将okhttp和网络层连接起来。

 static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.transmitter = new Transmitter(client, call);
    return call;
  }

RealCall.execute()/RealCall.enqueue()

  1. 调用Dispatcher.executed()/enqueue()将call加入到执行的Call队列中,以控制Call的启动、终结和取消。

  2. 调用getResponseWithInterceptorChain()开始向服务器发送请求并获取回应

    @Override public void enqueue(Callback responseCallback) {
        ...
        transmitter.callStart();
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
      }
    

RealCall.getResponseWithInterceptorChain()

OkHttp将各种请求信息的封装写在了不同层的Interceptor中,根据开放方法的参数设置,请求时层层添加http协议规定的信息,最终将请求发出。getResponseWithInterceptorChain()方法的主要逻辑如下:

  1. 添加自定义的interceptor

  2. 依次添加自定义interceptor、RetryAndFollowUpInterceptor、BridgeInterceptor、CacheInterceptor、ConnectInterceptor、自定义的networkInterceptor(如Facebook的stetho),

  3. 创建RealInterceptorChain对象,之后调用RealCall.RealInterceptorChain.proceed()`方法,按照责任链序依次调用每个拦截器处理逻辑

    Response getResponseWithInterceptorChain() throws IOException {
        List<Interceptor> interceptors = new ArrayList<>();
        interceptors.addAll(client.interceptors());
        interceptors.add(new RetryAndFollowUpInterceptor(client));
        interceptors.add(new BridgeInterceptor(client.cookieJar()));
        interceptors.add(new CacheInterceptor(client.internalCache()));
        interceptors.add(new ConnectInterceptor(client));
        if (!forWebSocket) {
          interceptors.addAll(client.networkInterceptors());
        }
        interceptors.add(new CallServerInterceptor(forWebSocket));
        Interceptor.Chain chain = new RealInterceptorChain();
    
        try {
          Response response = chain.proceed(originalRequest);
          ...
          return response;
        } catch (IOException e) {
          ...
        } 
      }
    

RealInterceptorChain.proceed()

  1. 检查自定义的interceptor是否合法

  2. 创建新的RealInterceptorChain,注意index+1

  3. 获取index当前的interceptor,并调用interceptor.intercept()方法交给Interceptor处理,获取返回值,思考一下为什么不能用循环

  4. 要求所有自定义的interceptor要调用RealInterceptorChain.proceed(),否则会造成请求无法发出

  5. 判断response或者response.body()是否为空

    public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
          throws IOException {
        ...
        // Call the next interceptor in the chain.
        RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
            index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
        Interceptor interceptor = interceptors.get(index);
        Response response = interceptor.intercept(next);
    
        // Confirm that the next interceptor made its required call to chain.proceed().
        if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
          throw new IllegalStateException("network interceptor " + interceptor
              + " must call proceed() exactly once");
        }
    
        // Confirm that the intercepted response isn't null.
        if (response == null) {
          throw new NullPointerException("interceptor " + interceptor + " returned null");
        }
    
        if (response.body() == null) {
          throw new IllegalStateException(
              "interceptor " + interceptor + " returned a response with no body");
        }
    
        return response;
      }
    

参考

https://square.github.io/okhttp/

https://zhuanlan.zhihu.com/p/58093669

https://www.jianshu.com/p/8d69fd920166

https://juejin.cn/post/6844904037154816007

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

许进进

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值