okhttp源码简单流程分析

news2024/11/25 0:14:30

拦截器详细解析可以看大佬简书 "https://www.jianshu.com/p/6fac73f7570f"和 “https://www.jianshu.com/p/3c740829475c”

在这里插入图片描述

okhttp请求流程
1:OkHttpClient okHttpClient = new OkHttpClient.Builder()
构建一个okhttpClient对象,传入你想传入的对象,不传就是默认的;
2:构建request对象
Request request = new Request.Builder()
3:okHttpClient.newCall 实际上返回的realCall类 继续调用RealCall.newRealCall
4:调用enqueue方法,传入我们需要的回调接口,而且会判断,
synchronized (this) {
if (executed) throw new IllegalStateException(“Already Executed”);
executed = true;
}
如果当前这个call对象已经被运行的话,则抛出异常;
5:继续调用dispatcher的enqueue方法,如果当前运行队列<64并且正在运行,访问同一个服务器地址的请求<5
就直接添加到运行队列,并且开始运行;
不然就添加到等待队列;
6:运行AsyncCall,调用它的execute方法
7:在execute方法中处理完response之后,会在finally中调用dispathcer的finished方法;
8:当当前已经处理完毕的call从运行队列中移除掉;并且调用promoteCalls方法
9:promoteCalls方法中进行判断,
如果运行队列数目大于等于64,如果等待队列里啥都没有,也直接return?
循环等待队列,
将等待队列中的数据进行移除,移除是根据运行队列中还能存放多少来决定;
移到了运行队列中,并且开始运行;

OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
        Request request = new Request.Builder().build();
        //newCall方法是调用RealCall的newRealCall返回一个RealCall
        Call call = okHttpClient.newCall(request);
        //执行请求
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }
        });
        //这里第二次执行会直接报IllegalStateException错误并提示Already Executed
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                
            }
        });

进入call.enqueue方法

@Override public void enqueue(Callback responseCallback) {
	//这个锁是为了防止重复请求 如果你同一个call enqueue多次就会直接返回Already Executed
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    //这里的enqueue是传入任务线程AsyncCall并分配到运行队列或等待队列
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

进入enqueue方法

//okhttp里执行任务分为两个队列 运行队列和等待队列
synchronized void enqueue(AsyncCall call) {
	//当运行的队列中的数值小于64, 
	//并且同时访问同一个机器目标HOST请求书小于5直接加入到运行队列不然的话就加入到等待队列
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      //这里维护着请求用的线程池
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }
  public synchronized ExecutorService executorService() {
    if (executorService == null) {
    //1:核心线程数 保持在线程池中的线程数量
	//2:线程池最大可容纳的线程数  
	//3参数:当线程池中的线程数量大于核心线程数,空闲线程就会等待60s才会被终止,如果小于就会立刻停止;
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
//这个就是队列中传入的线程NameRunnable继承自Runnable
final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    RealCall get() {
      return RealCall.this;
    }

	//这个就是Async的run方法
    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
      	//getResponseWithInterceptorChain() 添加拦截器 okhttp的责任链设计也在这里
      	//response就是请求结果
        Response response = getResponseWithInterceptorChain();
        //判断重试/重定向拦截器是否被关闭
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          //返回结果
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
      	//将执行完成的任务从队列中移除
        client.dispatcher().finished(this);
      }
    }
  }

添加拦截器

//添加拦截器 责任链设计 
//拦截器详细解析可以看大佬简书 "https://www.jianshu.com/p/6fac73f7570f"和 "https://www.jianshu.com/p/3c740829475c"
Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    //重试/重定向拦截器 连接失败后进行重试、对请求结果跟进后进行重定向
    interceptors.add(retryAndFollowUpInterceptor);
    //桥拦截器:连接应用程序和服务器的桥梁,我们发出的请求会经过它的处理才能发给服务器,
    //比如设置请求内容的长度 封装header属性 host keep-live gzip header 进行基本设置,
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //缓存拦截器 在发出请求前,先判断是否命中缓存,
    //如果命中则可以不请求,直接使用缓存的响应(默认只会对Get请求进行缓存);
    //如果未命中则进行网络请求,并将结果缓存,等待下次请求被命中。
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //连接拦截器 与服务器建立连接。
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    //与服务器通信;封装请求数据与解析响应数据(如:HTTP报文)。
    interceptors.add(new CallServerInterceptor(forWebSocket));
	
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
	//执行拦截器
    return chain.proceed(originalRequest);
  }

在execute方法中处理完response之后,会在finally中调用dispathcer的finished方法;

//将当前已经处理完毕的call从运行队列中移除掉;并且调用promoteCalls方法
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

promoteCalls方法中进行判断

//如果运行队列数目大于等于64,如果等待队列里啥都没有,也直接return
//循环等待队列,
//将等待队列中的数据进行移除,移除是根据运行队列中还能存放多少来决定;
//移到了运行队列中,并且开始运行;

private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

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

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

相关文章

Wlan——锐捷AC冗余方案讲解(集群AC、热备AC、VAC)

目录 无线AC集群 AC集群的原理 AC集群的实现方式与配置 通过AC集群实现负载分担 AP的Failover优先级 优缺点 无线AC热备 AC热备的基本概念 AC热备工作原理 保活报文类型 热备工作模式 热备AC的配置 故障检查 优缺点 无线VAC VAC的基本概念 VSL链路和业务链路 …

软件需求-架构师之路(五)

软件需求 软件需求&#xff1a; 指用户 对系统在功能、行为、性能、设计约束等方面的期望。 分为 需求开发 和 需求管理 两大过程。 需求开发&#xff1a; 需求获取需求分析需求定义&#xff08;需求规格说明书&#xff09;需求验证&#xff1a;拉客户一起评审&#xff0c…

Vim在Mac电脑中的下载与安装方法:MacVim

本文介绍在Mac系统电脑中&#xff0c;下载、安装文本编辑器Vim软件&#xff08;MacVim软件&#xff09;的具体方法。 在Mac系统电脑中&#xff0c;原本就带有一个非图形界面的Vim&#xff1b;只要我们在终端中&#xff0c;输入如下的代码&#xff0c;就可以查看系统自带的非图形…

win10在vmware16.2.3上安装macos13.1系统

第一步、安装vmware版本信息如下 第二步、下载unlocker426放到安装文件夹 第三步、管理员身份运行unlock.exe 第四步、运行vmware新建虚拟机 第五步、启动新创建的虚拟机macOS13.1并选择语言 第六步、选择磁盘工具抹掉格式化安装磁盘 第七步、格式化完成后退出磁盘工具 第八步、…

网工内推 | 外企网工,带专业培训,NP认证优先

01 广州开讯通信技术有限公司 &#x1f537;招聘岗位&#xff1a;网络工程师 &#x1f537;职责描述&#xff1a; 1、负责组织制定系统集成项目的技术方案编写、标书的准备、讲解及用户答疑等工作; 2、配合客户经理完成与用户的技术交流、技术方案宣讲、系统演示等工作; 3、配…

【第三阶段】kotlin语言中的==与===比较操作

"" 内容的比较 相当于Java的equals() "" 引用的比较 1. fun main() {val str1"kotlin"val str2"kotlin"//java写法println(str1.equals(str2))//kt写法println(str1str2)//结论 str1.equals(str2)等价于str1str2 }执行结果 2. fu…

appium2 安装 和出现问题解决

1.安装环境 A macOS, Linux, or Windows operating systemNode.js version in the SemVer range ^14.17.0 || ^16.13.0 || >=18.0.0NPM version >= 8 (NPM is usually bundled with Node.js, but can be upgraded independently) 1.1只要安装nodejs最新版就好了 1.2安…

leetcode473. 火柴拼正方形(回溯算法-java)

火柴拼正方形 leetcode473 火柴拼正方形题目描述回溯算法 上期经典算法 leetcode473 火柴拼正方形 难度 - 中等 原题链接 - leetcode473 火柴拼正方形 题目描述 你将得到一个整数数组 matchsticks &#xff0c;其中 matchsticks[i] 是第 i 个火柴棒的长度。你要用 所有的火柴棍…

解决ios隔空播放音频到macos没有声音的问题

解决ios隔空播放音频到macos没有声音的问题 一、检查隔空播放支持设备和系统要求二、打开隔空播放接收器三、重置MAC控制中心进程END 一、检查隔空播放支持设备和系统要求 Mac、iPhone、iPad 和 Apple Watch 上“连续互通”的系统要求 二、打开隔空播放接收器 ps;我设备是同一…

第十三章 SpringBoot项目(总)

1.创建SpringBoot项目 1.1.设置编码 1.4.导入已有的spring boot项目 2.快速搭建Restfull风格的项目 2.1.返回字符串 RestController public class IndexController {RequestMapping("/demo1")public Object demo1() {System.out.println("demo1 ran...."…

蝉联第一 | 2022中国数据泄露防护市场份额报告发布

2022年天空卫士DLP 市场份额位列中国第一 IDC 2022中国数据泄露防护市场份额报告于8月14日发布。报告主要阐述中国数据泄露防护市场的规模、厂商份额以及技术发展变化等内容。报告数据显示&#xff0c;2022年中国中国数据泄露防护&#xff08;DLP&#xff09;市场规模为1.31亿…

智慧工地云平台源码——塔机监控系统

智慧工地概念 智慧工地是一种崭新的工程全生命周期管理理念&#xff0c;是指运用信息化手段&#xff0c;通过对工程项目进行精确设计和施工模拟&#xff0c;围绕施工过程管理&#xff0c;建立互联协同、智能生产、科学管理的施工项目信息化生态圈&#xff0c;并将此数据在虚拟…

【Python程序设计】基于Python Flask的全国气象数据采集及可视化系统-附下载方式以及项目参考论文,原创项目其他均为抄袭

基于Python Flask的全国气象数据采集及可视化系统 一、项目简介二、项目技术三、项目功能四、运行截图五、分类说明六、实现代码七、数据库结构八、源码下载地址 一、项目简介 本项目是一个基于Web技术的实时气象数据可视化系统。通过爬取中国天气网的各个城市气象数据&#x…

linkis 1.1.1 报错 No plugin found spark-2.4.8, please check your configuration

按照官方教程设置,但是仍然报错 Caused by: java.util.concurrent.ExecutionException: LinkisException{errCode70063, descNo plugin found spark-2.4.8, please check your configuration, iphadoop0004, port9103, serviceKindlinkis-cg-engineplugin} 这个时候,我们首先检…

配置pyqt5开发环境

安装库 pip install pyqt5 -i https://mirrors.aliuyun.com/pypi/simple pip install pyqt5-tools -i https://mirrors.aliuyun.com/pypi/simple pip install PyQt5designer -i https://mirrors.aliuyun.com/pypi/simple配置External Tools Name:QtDesigner Program:C:\Anaco…

第九章 动态规划part10(代码随想录)

121. 买卖股票的最佳时机 1. 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 用二维dp数组表示第i天的2种状态 dp[i][0] 表示第i天持有股票所得最多现金&#xff0c;可能i-1天就买股票了 dp[i][1] 表示第i天不持有股票所得最多现金 最后求&#xff1a;dp[len-1][0…

SQLServer数据库获取单表重复记录中日期最新的记录

SQLServer数据库获取重复记录中日期最新的记录 CREATE TABLE [dbo].[t_expense_record_info]([id] [int] IDENTITY(1,1) NOT NULL,[goods_id] [int] NULL,[amount] [decimal](18, 6) NULL,[expense_time] [datetime] NULL,[user_id] [int] NULL,[create_date] [datetime] NULL…

qt初入门0:结构体中QString用memset导致崩溃分析及QLatin1String简单查看源码

初识Qt,进行开发时遇到一个崩溃问题 简单整理 1&#xff1a;问题描述如下&#xff0c;结构体中QString成员&#xff0c;然后对结构体调用了memset导致问题&#xff1a; 2&#xff1a;问题分析&#xff0c;加断点调试的方式可以明确分析到行数 可以明确看出&#xff0c;初始化…

msvcr120.dll丢失怎么修复,介绍几种最简单的修复方法

当您在运行某个程序时遇到msvcr120.dll丢失的错误提示时&#xff0c;可能无法正常启动或执行该程序。msvcr120.dll是Microsoft Visual C Redistributable中的一个动态链接库&#xff08;DLL&#xff09;文件&#xff0c;它是一种运行时库&#xff08;Runtime Library&#xff0…

浅谈时序:set_input_delay

1、set_input_delay的本质 set_input_delay是对模块input信号在模块外部延迟的约束&#xff0c;本质上EDA工具会根据约束调整设计内部的器件类型&#xff0c;摆放的位置以及优化内部组合逻辑保证满足约束要求。 约束指导原则&#xff1a;尽量照顾设计外部逻辑延时 set_input…