OkHttp3源码分析(三)

基本使用——OkHttp3详细使用教程 概述 OkHttp现在应该算是最火的Http第三方库,Retrofit底层也是使用OkHttp,网上很多教程都写的不错,但是有些我认为重要的知识,大多一笔带过,所以我决定写一篇入门文章 OkHttp官网地址:http://square.github.io/okhttp/ OkHttp GitHub地址:https://github.com/square/okhttp 出现背景 网络... 阅读详情

上一篇Okhttp3源码分析(二)讲了核心类Dispatcher,下面继续往深层次讲述Okhttp3的奥秘

一、HttpEngine(Http引擎类)
首先看一下构造函数

public HttpEngine(OkHttpClient client, Request request, boolean bufferRequestBody,
      boolean callerWritesRequestBody, boolean forWebSocket, StreamAllocation streamAllocation,
      RetryableSink requestBodyOut, Response priorResponse) {
    this.client = client;
    this.userRequest = request;
    this.bufferRequestBody = bufferRequestBody;
    this.callerWritesRequestBody = callerWritesRequestBody;
    this.forWebSocket = forWebSocket;
    this.streamAllocation = streamAllocation != null
        ? streamAllocation
        : new StreamAllocation(client.connectionPool(), createAddress(client, request));
    this.requestBodyOut = requestBodyOut;
    this.priorResponse = priorResponse;
  }
  • client 声明的OkHttpClient
  • request 声明的Request
  • streamAllocation 管理connect的类
  • requestBodyOut 用于重试时候
  • priorResponse 用于重定向时候返回的response

看一下sendRequest()

public void sendRequest() throws RequestException, RouteException, IOException {
    if (cacheStrategy != null) return; // Already sent.
    if (httpStream != null) throw new IllegalStateException();

    //组装header
    Request request = networkRequest(userRequest);
    //这个其实就是Cache里的final InternalCache internalCache = new InternalCache()
    InternalCache responseCache = Internal.instance.internalCache(client);
    //获取缓存的response
    Response cacheCandidate = responseCache != null
        ? responseCache.get(request)
        : null;

    long now = System.currentTimeMillis();
    cacheStrategy = new CacheStrategy.Factory(now, request, cacheCandidate).get();
    //就是传入的request
    networkRequest = cacheStrategy.networkRequest;
    //传入的cacheCandidate转化的cacheResponse
    cacheResponse = cacheStrategy.cacheResponse;

    //不会null代表OkHttpClient设置了cache
    if (responseCache != null) {
      //Cache.trackResponse(cacheStrategy) 对networkRequest和cacheResponse进行判断
      responseCache.trackResponse(cacheStrategy);
    }

    //缓存的response!=null,但是cacheResponse==null,可判断该缓存已经失效
    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }

    // If we're forbidden from using the network and the cache is insufficient, fail.
    //都为null 组装userResponse
    if (networkRequest == null && cacheResponse == null) {
      userResponse = new Response.Builder()
          .request(userRequest)
          .priorResponse(stripBody(priorResponse))
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(EMPTY_BODY)
          .build();
      return;
    }

    // If we don't need the network, we're done.
    if (networkRequest == null) {
      //设置userResponse为缓存的response
      userResponse = cacheResponse.newBuilder()
          .request(userRequest)
          .priorResponse(stripBody(priorResponse))
          .cacheResponse(stripBody(cacheResponse))
          .build();
      userResponse = unzip(userResponse);
      return;
    }

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    boolean success = false;
    try {
      //Http1xStream对象 真正socket连接 tcp连接的地方
      httpStream = connect();
      httpStream.setHttpEngine(this);

      //不重定向的时候为false,反之为true
      if (writeRequestHeadersEagerly()) {
        long contentLength = OkHeaders.contentLength(request);
        if (bufferRequestBody) {
          if (contentLength > Integer.MAX_VALUE) {
            throw new IllegalStateException("Use setFixedLengthStreamingMode() or "
                + "setChunkedStreamingMode() for requests larger than 2 GiB.");
          }

          if (contentLength != -1) {
            // Buffer a request body of a known length.
            httpStream.writeRequestHeaders(networkRequest);
            requestBodyOut = new RetryableSink((int) contentLength);
          } else {
            // Buffer a request body of an unknown length. Don't write request headers until the
            // entire body is ready; otherwise we can't set the Content-Length header correctly.
            requestBodyOut = new RetryableSink();
          }
        } else {
          httpStream.writeRequestHeaders(networkRequest);
          requestBodyOut = httpStream.createRequestBody(networkRequest, contentLength);
        }
      }
      success = true;
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (!success && cacheCandidate != null) {
        //没有成功,但是有缓存,清除缓存
        closeQuietly(cacheCandidate.body());
      }
    }
  }
private Request networkRequest(Request request) throws IOException {
    Request.Builder result = request.newBuilder();

    if (request.header("Host") == null) {
      result.header("Host", hostHeader(request.url(), false));
    }

    if (request.header("Connection") == null) {
      result.header("Connection", "Keep-Alive");
    }

    if (request.header("Accept-Encoding") == null) {
      transparentGzip = true;
      result.header("Accept-Encoding", "gzip");
    }

    List<Cookie> cookies = client.cookieJar().loadForRequest(request.url());
    if (!cookies.isEmpty()) {
      result.header("Cookie", cookieHeader(cookies));
    }

    if (request.header("User-Agent") == null) {
      result.header("User-Agent", Version.userAgent());
    }

    return result.build();
  }

主要做了

  • 组装request的header
  • 判断是否有可用的缓存,有的话就赋值给userResponse
  • 没有可用缓存发起socket连接
  • 有重定向的时候组装一些参数
  • 如果连接失败并且有缓存,就清除缓存

然后再看一下readResponse

public void readResponse() throws IOException {
    if (userResponse != null) {
      return; // Already ready.
    }
    if (networkRequest == null && cacheResponse == null) {
      throw new IllegalStateException("call sendRequest() first!");
    }
    if (networkRequest == null) {
      return; // No network response to read.
    }

    Response networkResponse;

    if (forWebSocket) {
      httpStream.writeRequestHeaders(networkRequest);
      networkResponse = readNetworkResponse();
    } else if (!callerWritesRequestBody) {
      //网络拦截器 对response进行封装
      networkResponse = new NetworkInterceptorChain(0, networkRequest).proceed(networkRequest);
    } else {
      // Emit the request body's buffer so that everything is in requestBodyOut.
      if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) {
        bufferedRequestBody.emit();
      }

      // Emit the request headers if we haven't yet. We might have just learned the Content-Length.
      if (sentRequestMillis == -1) {
        if (OkHeaders.contentLength(networkRequest) == -1
            && requestBodyOut instanceof RetryableSink) {
          long contentLength = ((RetryableSink) requestBodyOut).contentLength();
          networkRequest = networkRequest.newBuilder()
              .header("Content-Length", Long.toString(contentLength))
              .build();
        }
        httpStream.writeRequestHeaders(networkRequest);
      }

      // Write the request body to the socket.
      if (requestBodyOut != null) {
        if (bufferedRequestBody != null) {
          // This also closes the wrapped requestBodyOut.
          bufferedRequestBody.close();
        } else {
          requestBodyOut.close();
        }
        if (requestBodyOut instanceof RetryableSink) {
          httpStream.writeRequestBody((RetryableSink) requestBodyOut);
        }
      }

      //读取网络请求回来的response
      networkResponse = readNetworkResponse();
    }

    receiveHeaders(networkResponse.headers());

    // If we have a cache response too, then we're doing a conditional get.
    //不为null,缓存更新为最新的networkResponse
    if (cacheResponse != null) {
      if (validate(cacheResponse, networkResponse)) {
        userResponse = cacheResponse.newBuilder()
            .request(userRequest)
            .priorResponse(stripBody(priorResponse))
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();
        releaseStreamAllocation();

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        InternalCache responseCache = Internal.instance.internalCache(client);
        responseCache.trackConditionalCacheHit();
        responseCache.update(cacheResponse, stripBody(userResponse));
        userResponse = unzip(userResponse);
        return;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }

    //根据networkResponse组装userResponse
    userResponse = networkResponse.newBuilder()
        .request(userRequest)
        .priorResponse(stripBody(priorResponse))
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    //如果有body
    if (hasBody(userResponse)) {
      maybeCache();
      //看后端是否支持Gzip,根据Gzip压缩
      userResponse = unzip(cacheWritingResponse(storeRequest, userResponse));
    }
  }

主要做了

  • 网络拦截器 进行相应处理
  • networkResponse中的cookies存入OkHttpClient中的cookieJar中,该对象默认是空的,如果我们在构造OkHttpClient的时候给予它一个CookieJar那么OkHttpClient就会将每次获得的cookie都存入我们定义的CookieJar中。一般是把url作为key,cookies作为value
  • 如果该请求有缓存就更新缓存
  • 根据networkResponse组装userResponse
  • 看是否需要缓存 只缓存GET方法

看一下readResponse()里的readNetworkResponse()

private Response readNetworkResponse() throws IOException {
    httpStream.finishRequest();

    Response networkResponse = httpStream.readResponseHeaders()
        .request(networkRequest)
        .handshake(streamAllocation.connection().handshake())
        .header(OkHeaders.SENT_MILLIS, Long.toString(sentRequestMillis))
        .header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis()))
        .build();

    if (!forWebSocket) {
      networkResponse = networkResponse.newBuilder()
          .body(httpStream.openResponseBody(networkResponse))
          .build();
    }

    if ("close".equalsIgnoreCase(networkResponse.request().header("Connection"))
        || "close".equalsIgnoreCase(networkResponse.header("Connection"))) {
      streamAllocation.noNewStreams();
    }

    return networkResponse;
  }

上面就是组装networkResponse

okhttp3源码分析之AsyncTimeout 继承自Timeout, 使后台线程在发生超时时准确执行操作。用来实现本机不支持的超时。 公开方法: final void enter() 调用了private static synchronized void scheduleTimeout,确定链表关系(当前node上个的next和this的header前后关系)。 private static synchronized void s... 阅读详情

相关推荐

OKHttp3源码分析——学习笔记

文章目录1. HttpClient与HttpUrlConnection的区别2. OKHttp源码分析使用步骤:dispatcher任务调度器,Request请求RealCallAsyncCall3. OKHttp架构分析1. 异步请求线程池, Dispather2. 连接池清理线程池- ConnectionPool3. 缓存整理线程池 DisLruCache4. Http2异步事务线程池, ht...

Sincerity_的博客 1380

okhttp3使用

一、引入包在项目module下的build.gradle添加okhttp3依赖compile 'com.squareup.okhttp3:okhttp:3.3.1'二、基本使用1、okhttp3 Get 方法1.1 、okhttp3 同步 Get方法/** * 同步Get方法 */ private void okHttp_synchronousGet() { new Thread(new

Peak1Chen的博客 5万+

Okhttp源码分析&实践(三)【WebDemo的简单实现】

经过上面Demo实践,相信大家已经对服务端web编程有了一点点的了解,当然这是特别简单的一个demo而已,并非服务端web编程就是如此的简单,要想学好、用好web编程,里面的东西很多、很多。不过我们仅仅是为了配合okhttp编程实践,去自己会实现简单的服务端demo 测试即可,如有兴趣的小伙伴,可以看一下其他书籍,深入学习一下这块知识。

itbird的专栏 736

OkHttp3简单使用:请求和响应,post,get

一,HTTP请求、响应报文格式 要弄明白网络框架,首先需要先掌握Http请求的,响应的报文格式。 HTTP请求报文格式: HTTP请求报文主要由请求行、请求头部、请求正文3部分组成. request.png   请求行:由请求方法,URL,协议版本三部分构成,之间用空格隔开 请求方法包括:POST、GET、HEAD、PUT、POST、TRACE、OPTIONS、DELETE等 ...

gjy_it的博客 4383

避坑指南:Ubuntu 22.04上源码编译Autoware.universe(Humble版)的完整流程与常见错误修复

本文详细介绍了在Ubuntu 22.04上源码编译Autoware.universe(Humble版)的完整流程与常见错误修复方法。从环境准备、依赖安装到编译优化,提供了实用的避坑指南和解决方案,帮助开发者高效完成安装并避免常见问题。特别针对源码安装过程中的疑难杂症给出了具体修复建议。

weixin_33712987的博客 382

基于ESP32-P4的低成本旋转式激光雷达系统设计

激光雷达(LiDAR)作为机器人环境感知的核心传感器,其本质是融合光学测距、机电控制与实时数据处理的嵌入式系统。理解时间飞行(ToF)原理与机械旋转耦合机制,是实现高精度点云采集的基础;而系统级误差源——如计时抖动、电机惯性、信号串扰和温漂——直接决定实际建图鲁棒性。在资源受限场景下,选用RISC-V双核MCU(如ESP32-P4)可兼顾实时控制与网络服务能力,通过硬件时间戳捕获、S型加减速轨迹规划、旋转变压器供电及双Bank OTA等工程手段,有效平衡分辨率、帧率与稳定性。本文围绕360°旋转式激光雷达的

weixin_36231030的博客 432

OkHttp3源码(六) ------ Response

Response类 —— 主要是对响应报文的概念的实现。 响应报文的结构图 可以看到响应报文的结构和请求报文的结构差不多。只是具体细节有些不同。 我们根据响应报文的结构来了解Response源码。 1、Respons的属性 //得到该响应报文的请求报文 final Request request; //返回响应报文用的协议,这个是个枚举类。------对应上图协议版本 final P...

look_Future的博客 2601

一种基于因果干预的少样本学习的故障诊断模型

本文提出CIRNet,一种融合元学习与因果干预的少样本故障诊断框架。针对传统方法易学习伪相关特征的问题,CIRNet通过构建结构因果模型,采用类级和特征级双调整策略消除先验知识干扰。实验在多个轴承/齿轮数据集上验证其有效性,在跨组件任务中准确率最高达93.41%,特征可视化显示更好的类可分性。相比现有方法,CIRNet显著提升了模型泛化能力和可解释性。

hasakie的博客 955

OkHttp3.0(二)-OkHttpClient类、Request类、Call类、Response的简单分析

目录 1,OkHttpClient 2,Request 2.1,RequestBody 2.1.1,FormBody 2.1.2,MultipartBody 2.2,Headers Call 同步请求 异步请求 Response 通过上一节 OkHttp3.0(一)-OkHttp的基本使用 ,我们掌握了OkHttp的基本使用,如何发送同步请求、异步请求,如何发送GET请求、P...

Lone_Star斌 的博客 1万+

零基础玩转IndexTTS2:5分钟搭建个人语音克隆工具(支持B站同款情感语音)

本文详细介绍了如何零基础使用IndexTTS2快速搭建个人语音克隆工具,支持B站同款情感语音。IndexTTS2作为自回归零样本TTS系统,突破传统技术限制,实现精准时长控制和丰富情感表达。通过HuggingFace在线演示,用户可轻松克隆音色并调节情感参数,适用于自媒体配音、有声书朗读等场景。

purple的博客 917

PID参数自整定实战解析:从继电反馈到智能调节的完整实现

本文深入解析PID参数自整定的实战应用,从继电反馈原理到智能调节算法的完整实现。通过详细讲解振荡波形特征提取、参数计算的工程化改进及工业场景调试技巧,帮助工程师快速掌握PID自整定技术,提升控制系统性能。特别针对嵌入式系统优化和噪声环境下的稳定策略提供了实用解决方案。

weixin_28731223的博客 461

okhttp3.7源码分析(三)-多路复用

前面两篇文章我们主要了解了一下okhttp的同步异步使用、基本工作原理及okhttp的拦截器的功能。 今天我们重点分析一下ConnectIntercept拦截器中的多路复用 @Override public Response intercept(Chain chain) throws IOException { RealInterceptorChain realChain = (Rea...

weixin_43843686的博客 1398

量化交易 米筐 多因子合成(回测中使用)

多因子合成 1、因子合成 将一些相关(高、低)的因子合成一个因子 方法: 使用PCA方式(使用于较强相关性) 2、核心代码 将因子 inc_revenue_ttm 和 operating_revenue_growth_ratio_ttm 合成为一个因子 inc_operating from sklearn.decomposition import PCA pca = PCA(n_components=1) # 因子合并 fund['inc_operating'] = pca.fit_transform(fu

廷益_飞鸟的博客 1062

OkHttp 3.7源码分析(三)——任务队列

OkHttp3.7源码分析文章列表如下: OkHttp源码分析——整体架构 OkHttp源码分析——拦截器 OkHttp源码分析——任务队列 OkHttp源码分析——缓存策略 OkHttp源码分析——多路复用 前面的博客已经提到过,OkHttp的一个高效之处在于在内部维护了一个线程池,方便高效地执行异步请求。本篇博客将详细介绍OkHttp的任务队列...

weixin_34107955的博客 203

【Py/Java/C++/C/JS/Go六种语言OD独家2024E卷真题】20天拿下华为OD笔试之【固定滑窗】2024E-考勤信息【欧弟算法】全网注释最详细分类最全的华为OD真题题解

可上练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【优快云】否则不通过)

weixin_48157259的博客 618

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

OkHttp 3.7源码分析(三) - 任务队列

前面的博客已经提到过,OkHttp的一个高效之处在于在内部维护了一个线程池,方便高效地执行异步请求。本篇博客将详细介绍OkHttp的任务队列机制。 1.线程池的优点 OkHttp的任务队列在内部维护了一个线程池用于执行具体的网络请求。而线程池最大的好处在于通过线程复用减少非核心任务的损耗。 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显着减少处理器单元的闲置时间,增加

嘻哈包袱铺 专栏 534

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Android OkHttp3源码分析

title: Android OkHttp3源码分析 date: 2019-06-03 21:16:50 tags: Android第三方框架 文章目录title: Android OkHttp3源码分析 date: 2019-06-03 21:16:50 tags: Android第三方框架Android OkHttp3源码分析介绍源码分析简单使用OkHttpClient的创建Request的...

hesheng1024的博客 2662

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

深入解析OkHttp3

OkHttp是一个精巧的网络请求库,有如下特性: 1)支持http2,对一台机器的所有请求共享同一个socket 2)内置连接池,支持连接复用,减少延迟 3)支持透明的gzip压缩响应体 4)通过缓存避免重复的请求 5)请求失败时自动重试主机的其他ip,自动重定向 6)好用的API其本身就是一个很强大的库,再加上Retrofit2、Picasso的这一套组合拳,使其愈发的受到开发者的关注

一个码农的博客 6万+

Okhttp3源码分析之一

一、OKHTTP简介 OKHttp是一个处理网络请求的开源项目OKHttp GitHub地址 OKHttp优点 支持HTTP2/SPDY(SPDY是Google开发的基于TCP的传输层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。) socket自动选择最好路线,并支持自动重连,拥有自动维护的socket连接池,减少握手次数,减少了请求延迟,共享Socket,减少对服务器...

jy专栏 334

开源库源码分析:Okhttp源码分析(一)

接下来就要开始分析一些常用开源库的源码了,作为最常用的网络请求库,OkHttp以其强大的功能深受Android开发者的喜爱(比如说我),还有对该库进行二次封装而成的热门库,比如说Retrofit。本文我们将从源码入手看看OkHttp是如何运作的。注意本文解析的是OkHttp3库,该库是用Kotlin写的,需要大家有一些Kotlin基础。

Tai_Monster的博客 4525

OkHttp3源码分析

OKHTTP介绍 在前一篇文章中已经初步介绍了OKHTTP框架以及基本的使用,不熟悉的读者可以看我的上一篇文章OKHTTP基本使用,下面从OKHTTP的源码出发,分析整个请求的过程。笔者这里分析的OKHTTP版本是3.8.0 官方请求案例 OkHttpClient client = new OkHttpClient(); String run(String url) throws IOException { Request request = new Request.Builder() .

李林杰的博客 892
上一篇: OkHttp3源码分析(二)
下一篇: Java中的一些基础算法
豪豪6
博客等级 码龄14年 6粉丝 35原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值