OkHttp相关知识(二)

news2025/2/23 23:14:55

okhttp中一次网络请求的大致过程:

    1. Call对象对请求的封装
    1. dispatcher对请求的分发
    1. getResponseWithInterceptors()方法

一、OkHttp同步方法总结:

  1. 创建OkHttpClient和构建了携带请求信息的Request对象
  2. 将Request封装成Call对象
  3. 调用Call的execute()发送同步请求

call.execute实际上调用的是RealCall.execute()方法:

 @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.timeoutEnter();
    transmitter.callStart();
    try {
      client.dispatcher().executed(this);
      return getResponseWithInterceptorChain();  //这行语句的执行在后面拦截器链的介绍中会阐述
    } finally {
      client.dispatcher().finished(this);
    }
  }

其中client.dispatcher().executed(this);这个步骤就是将本次call请求加入到runningSyncCalls ArrayDeque队列中

  /** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

其中dispatcher中比较重要的几个变量:

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

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

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

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

OkHttp同步需要注意:

  • 送请求后,就会进入阻塞状态,直到收到响应。
    在这里插入图片描述

二、OkHttp异步方法总结

  1. 创建OkHttpClient和构建了携带请求信息的Request对象
  2. 将Request封装成Call对象
  3. 调用Call的enqueue方法进行异步请求

OkHttp异步需要注意:

  • OnResponse 和 onFailure工作在工作线程,即子线程

同步和异步的区别

    1. 发起请求的方法调用不同
      同步是调用execute(),异步是调用enqueue()
  • 2.阻塞线程与否
      同步会阻塞线程,异步不会阻塞线程,它会开启一个异步线程去完成网络请求的操作
    1. 同步请求就是执行请求的操作是阻塞式,直到Http响应返回
    1. 异步请求就类似非阻塞式的请求,它的执行结果一般都是通过接口回调的方式告知调用者

equeue()方法后续源码流程:

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

其中,AsyncCall其实就是一个Runnable,调用Dispatcher的enqueue()方法

void enqueue(AsyncCall call) {
    synchronized (this) {
      readyAsyncCalls.add(call);

      // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
      // the same host.
      if (!call.get().forWebSocket) {
        AsyncCall existingCall = findExistingCallWithHost(call.host());
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
      }
    }
    promoteAndExecute();
  }
 private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        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();
        executableCalls.add(asyncCall);
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }

    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }

其中,runningAsyncCalls表示正在运行的任务队列,主要用于判断并发表示的数量,readyAsyncCalls表示缓存等待请求的队列,
maxRequests = 64–>表示当前正在请求的Runnable最大数
maxRequestsPerHost = 5–>表示当前网络向同一个host请求的最大数

Enqueue方法总结:

    1. 判断当前call
    1. 封装成了一个AsyncCall对象
    1. client.dispatcher().enqueue()

Q1、OkHttp如何实现同步和异步请求?

发送的同步/异步请求都会在dispatcher中管理其状态

Q2:到底什么是dispatcher

dispatcher的作用为维护请求的状态,并维护一个线程池,用于执行请求。
在这里插入图片描述

Q3. 异步请求为什么需要两个队列

  • Deque<readyAsyncCalls> :正在等待执行,等待缓存的队列

  • Deque<runningAsyncCalls>:正在运行的任务队列

  • Dispatcher 生产者

  • ExecutorService:消费者池

OkHttp的任务调度

Call执行完肯定需要在runningAsyncCalls队列中移除这个线程

readyAsyncCalls队列中的线程在什么时候才会被执行呢?

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

    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        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();
        executableCalls.add(asyncCall);
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }

    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }

三、OkHttp拦截器

拦截器是OkHttp中提供一种强大机制,它可以实现网络监听、请求以及响应重写、请求失败重试等功能。
在这里插入图片描述

系统提供的内部拦截器链:

在这里插入图片描述

  • RetryAndFollowUpInterceptor
    重试和失败重定向拦截器,这个拦截器主要做些初始化的工作和创建一个StreamAllocation对象(OkHttp3.0代码中没有该对象),用来传递给后面的拦截器
    功能:
    (1)创建StreamAllocation对象
    (2)调用RealInterceptorChain.proceed(…)进行网络请求
    (3)根据异常结果或者响应结果判断是否要进行重新请求

    最大的请求次数 MAX_FOLLOW_UPS = 20

    (4)调用下一个拦截器,对response进行处理,返回给上一个拦截器

  • BridgeInterceptor
    桥接和适配拦截器
    功能:
    (1)是负责将用户构建的一个Request请求转化为能够进行网络访问的请求
    (2)将这个符合网络请求的Request进行网络请求
    (3)将网络请求回来的响应Response转化为用户可用的Response

  • CacheInterceptor
    缓存拦截器
    BridgeInterceptor和CacheInterceptor主要是用于补充用户请求创建过程中缺少一些必须的Http请求头和处理缓存的一些功能。

  • ConnectInterceptor
    连接拦截器,负责建立可用的连接,ConnectInterceptor是CallServerInterceptor的基础。
    (1) ConnectInterceptor获取Interceptor传过来的StreamAllocation,StreamAllocation.newStream()
    (2)将刚才创建的用于网络IO的RealConnection对象,以及对于与服务器交互最为关键的HttpCodec等对象传递给后面的拦截器
    流程功能:
    (1)创建一个RealConnection对象
    (2)选择不同的链接方式
    (3)CallServerInterceptor

  • CallServerInterceptor
    该适配器主要负责是将Http的请求写进网络的IO流中,并从网络IO流中读取服务端返回给客户端的数据。

RealCall.java

 Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    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(interceptors, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    boolean calledNoMoreExchanges = false;
    try {
      Response response = chain.proceed(originalRequest);
      if (transmitter.isCanceled()) {
        closeQuietly(response);
        throw new IOException("Canceled");
      }
      return response;
    } catch (IOException e) {
      calledNoMoreExchanges = true;
      throw transmitter.noMoreExchanges(e);
    } finally {
      if (!calledNoMoreExchanges) {
        transmitter.noMoreExchanges(null);
      }
    }
  }

OkHttp拦截器总结:

    1. 创建一系列拦截器,并将其放入一个拦截器list中,这list拦截器包含用户、网络和系统内部的拦截器
    1. 创建一个拦截器链RealInterceptorChain,并执行拦截器链的proceed方法。
      proceed方法的核心就是创建下一个拦截器
    1. 在发起请求前对request进行处理
    1. 调用下一个拦截器,获取response
    1. 对response进行处理,返回给上一个拦截器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/10432.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【11.16】Codeforces 刷题

DP\text{DP}DP &#xff1a;&#xff08;今天做的这两道都没啥 DP 相关来着 D. Match & Catch 题意&#xff1a; 给定两个字符串 1≤∣s1∣,∣s2∣≤50001\leq |s_1|,|s_2|\leq 50001≤∣s1​∣,∣s2​∣≤5000 &#xff0c;求最短的满足各只出现一次的连续公共字串。 思…

实验27:红外遥控三级控速风扇实验

今天介绍一个稍微复杂点的实验,复杂在设计和代码 ——OK,受了抖音西湖大学教授刺激,任何人都可以做研究 ——实验:红外遥控三级风速小电扇 ——每按一下CH-,风速从1-2-3-1-2-3-1循环 ——按下CH+,风扇停止 ——没有背景音乐目的是听风扇声音大小判断风速 OK实验介绍完了…

五个可以永远相信的神仙网站推荐

早八的我们是不是偶尔会处在焦虑中呢&#xff1f;一方面年轻人工作压力大&#xff0c;另一方面我们偶尔会感慨我们的碌碌无为&#xff0c;不知道怎样提升自己。今天为大家推荐五个焦虑时可以随手打开看&#xff0c;不知不觉悄悄提升自己的软件。 1.全历史 全历史是一个把历史以…

《元宇宙2086》亮相金鸡奖中国首部元宇宙概念院线电影启动

2022年中国金鸡百花电影节暨第35届中国电影金鸡奖于11月10日至12日在福建厦门举办&#xff0c;中国动漫集团控股子公司北京中文发文化发展有限公司与《元宇宙2086》作者高泽龙在金鸡奖创投论坛正式签约&#xff0c;宣布将共同启动筹拍中国首部元宇宙概念的院线电影。 当日下午&…

如何在Docker中安装MySQL数据库

1、Docker环境 视频教程&#xff1a;https://www.bilibili.com/video/BV1xv4y1S7kA 2、搜索镜像 https://hub.docker.com/网站搜索MySQL&#xff0c;确定其安装版本&#xff0c;这里安装8.0.31版&#xff1b; 3、拉取镜像 [rootlocalhost ~]# docker pull mysql:8.0.31 8.…

市级专精特新的申报条件

一、基本条件&#xff1a;&#xff08;各市政策不同具体情况也不同&#xff0c;下面为济南市企业的申报条件&#xff09; 1、连续经营3年以上&#xff0c;上年度企业营业收入在800万元以上&#xff1b; 2、近两年营业收入复合增长率不低于8%&#xff08;2021年参照国 家级调…

Nginx 反向代理

title: Nginx 反向代理 date: 2022-11-16 10:24 tags: [Nginx,反向代理,正向代理,代理] 文章目录〇、问题一、前言二、正向代理&反向代理2.1 正向代理2.2 反向代理三、Nginx配置反向代理参考更新〇、问题 什么是正向代理&#xff1f;什么是反向代理&#xff1f;Nginx如何配…

Mysql之视图、索引【第五篇】

大纲&#xff1a; 一、视图 1、什么是视图&#xff1f; 1) MySQL 视图(View)是一种虚拟的表&#xff0c;是从数据库中一个或多个表中导出来的表。视图由列和行构成&#xff0c;行和列的数据来自于定义视图的查询中所使用的表&#xff0c;并且还是在使用视图时动态生成的。 …

【蓝桥杯物联网赛项学习日志】Day3 关于IIC

经过昨天的学习&#xff0c;已经了解和初步学会配置CubeMax进行初始化配置。今天就开始下一章节的学习&#xff0c;关于IIC。 关键词&#xff1a;I2C OLED SSD1306 理论基础 串行通信接口通讯方式分&#xff0c;可以分为两种&#xff0c;分别是同步和异步。按照数据的传输方…

组成目标货币的最少张数

1、题目 arr 是货币数组&#xff0c;其中的值都是正数。再给定一个正数 aim。 每个值都认为是一张货币&#xff0c;返回组成 aim 的最少张数。 注意&#xff1a;因为是求张数&#xff0c;所以每张货币认为是相同或不同就不重要了。 2、思路 假设 arr [3&#xff0c;1&…

GD32F450的时钟笔记

GD32F450 标称 200MHz&#xff0c;但是在手册中又说 它是 240MHz。本文以 手册中的 240MHz 进行举例&#xff0c;我保险起见&#xff0c;产品中使用还是在 200MHz 下使用。 时钟树 手册上的时钟树图如下 GD32F450的 外部时钟源 有2个 LXTAL 外部低速时钟源 32.768 kHzHXTAL …

微信小程序登录获取不到头像和昵称解决办法!

微信小程序登录获取不到头像和昵称主要原因是&#xff1a;小程序wx.getUserProfile接口被收回&#xff01; 大家可以按照文档操作↓ PS&#xff1a; 针对小程序wx.getUserProfile接口将被收回后做出的授权调整 小程序文档中提出的调整说明 对于此次变化&#xff0c;现将小…

怎么使用股票委托下单接口?

都知道&#xff0c;在进行量化交易的时候&#xff0c;交易接口可以100%严格按照定制的计划执行交易&#xff0c;避免了出现下单犹豫和过量交易等问题&#xff0c;100%体现交易策略的收益性&#xff1b;今天主要来聊聊&#xff0c;关于怎么使用股票委托下单接口的问题&#xff1…

使用c#将aj-report桌面化1

说到底,aj-report是个工具,我想大多数人还是想快速使用它来创建一个可以展示的工具。通过之前的章节,你应该可以制作自己的报表页面了,下面我们来看看怎么把aj-report包装成一个桌面能够运行的软件。 当然作为扩展开发,受开源协议限制,我们不能大规模修改aj-report的源代…

[附源码]java毕业设计基于新高考模式下的排课系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

程序员“中年危机”?不,也可能是“人生转机”

程序员行业新技术发展迅猛&#xff0c;可以说是日新月异。也正是这个原因&#xff0c;中年危机成为我们必须面对和攻克的问题。 思考一个问题&#xff1a;那些能工作到45、50、甚至60的程序员们&#xff0c;究竟具备了哪些过人的能力&#xff1f; 就我过去的经历和观察来说&a…

【蓝桥杯真题练习】STEMA科技素养练习题库 答案版015 持续更新中~

1、牛顿运动定律描述了物体运动符合的规律,被誉为经典力学的基石。以下关于牛顿定律的描述中,不正确的选项是( )。 A、牛顿运动定律一共有三条 B根据牛顿定律,没有受到外力作用的物体运动速度会保持不变 C根据牛顿运动定律,两个物体作用于对方的力大小一定相等 D牛…

项目运营后阶段发力

前言 我记得之前有人跑过来问我&#xff1a;你知道为啥字节面试总会考算法吗&#xff1f;我没有回答&#xff0c;一方面是算法是大厂必考的东西&#xff0c;既然是必须的东西究竟个为什么有点白费心机&#xff0c;第二方面我没有进去过字节&#xff0c;所以我也拿捏不准为什么。…

业务数据分析-Excel的基础操作(一)

Excel的基础操作1、工作簿2、工作表3、数据基本操作4、数据类型5、常用快捷键1、工作簿 即Excel文档&#xff0c;用来储存和处理数据的文件 常用扩展名&#xff1a; xls 97-03版xlsx 07版以后xlsm 带宏的文件 一个工作簿包含多张工作表 2、工作表 默认名称&#xff1a;shee…

【小程序】微信小程序自定义组件Component详细总结

1- 前言 在本文中你将收获 小程序如何使用自定义组件自定义组件之间的传值自定义组件中插槽的使用 2- 组件文件新建 2.1 定义组件 在根目录新建components文件夹&#xff0c;建立cell 文件夹&#xff0c;右击创建cell的Component组件 cell.jscell.wxmlcell.jsoncell.wxss …