android源码宇宙——Okhttp

简介: android源码宇宙——Okhttp

Okhttp的使用


image.png


源码


阅读大纲

  1. 同步调用网络请求流程
  2. 异步调用网络请求流程
  3. Dispatcher类的代码逻辑
  4. 几个拦截器的逻辑


查看同步调用主流程

  1. 从newCall开始

调用RealCall.newRealCall创建Call

image.png

  1. RealCall.newRealCall

image.png

  1. 因为上一步中获取到的Call类型是RealCall,下面我们看看RealCall.execute

下一步我们先看dispatcher().executed然后再看getResponseWithInterceptorChain方法

image.png

  1. dispatcher().executed方法和getResponseWithInterceptorChain方法

dispatcher().executed方法

image.png

getResponseWithInterceptorChain方法

这个方法里面先构建了一个连接器,然后执行拦截器

image.png

  1. 同步调用完成

异步调用网络请求流程

  1. 业务代码中执行异步请求

image.png

  1. RealCall.enqueue

这个方法最终又调到了Dispatcher.enqueue

image.png

  1. Dispatcher.enqueue调度异步请求AsyncCall

image.png

  1. 上面的方法如果调用了executeorService.execute就开始执行线程池了,因为我们的AsyncCall实现了NameRunnable,然后NameRunnable又实现了Runnable,所以去看NameRunnable.run方法吧

image.png

  1. AsyncCall.execute执行请求

image.png


Dispatcher类的代码逻辑

Dispatcher的作用是对请求进行分发,也就是他是用来管理Call和分发Call的。同时他还维护了一个线程池

Dispatcher内部维护了三个队列:

  • runningSyncCalls同步请求队列   - 用于执行同步请求
  • runningAsyncCalls异步请求队列  -  用于执行异步请求
  • readyAsyncCalls等待中的请求队列  -  当异步请求并发数量达到限制时将请求放到等待队列中

Dispatcher如何调度异步逻辑

Dispatcher中没有太多的复杂逻辑,主要对其中的几个重要方法进行说明一下

  1. executorService获取线程池
  2. setMaxRequests设置支持的最大请求并发数,这个主要是相对于异步请求来说的,和同步请求没关系
  3. enqueue 调度异步请求
  4. cancelAll 取消所有请求
  5. promoteCalls 决定是否将等待队列中的请求加入到异步队列中进行执行
  6. executed 执行同步请求
  7. finished 两个finish方法,分别用来结束同步请求和异步请求

拦截器链的执行逻辑和几个拦截器内部的逻辑

拦截器执行逻辑也就是getResponseWithInterceptorChain方法

  1. 构建和执行责任链

需要说明的是client.interceptors()获取的拦截器无论如何都会被调用 而client.networkInterceptors()获取的拦截器只有请求走到真实网络请求之前才会调用(如果请求使用了缓存,或者真实请求开始之前结束了则不会调用)

image.png


  1. RealInterceptorChain.proceed方法

最终这里的逻辑可以保证对责任链进行执行

image.png


RetryAndFollowUpInterceptor重试,重定向拦截器

重定向拦截器实际上就是在内部维护了一个循环。如果当前请求支持重定向,那么会在当前的责任链位置重新执行责任链(会重试多次)。还有就是,它会尝试重用之前的连接

  1. 看intercept方法

image.png

image.png

下图中sameConnection实际上就是判断了一下请求的绝对host,port,scheme是否一致

image.png


BridgeInterceptor 桥拦截器

  • 桥拦截器会从用户的请求参数中,构建请求Request。主要是是对header中的内容进行补充完善,这一点帮我们省去了很多事情(比如使用HttpUrlconnection很多header参数需要我们自己处理)。
  • 然后使用这个请求访问网络,最后使用网络返回结果构建响应

intercept方法解读

  1. 处理请求header

image.png

image.png

  1. 请求获取响应并处理响应

image.png

CacheInterceptor缓存拦截器

主要做了如下几件事
  • 如果无法网络获取请求且缓存为空返回请求失败
  • 如果无法通过网络获取请求,且有缓存直接返回缓存
  • 如果请求结果返回304,说明缓存仍然在有效期内(返回的response中没有实体内容),直接把缓存返回并且刷新缓存
  • 如果网络请求返回了实体内容,并且请求是支持缓存的,则写入缓存
intercept源码

缓存拦截器会根据请求信息来决定是否使用缓存数据,并且在请求完成后决定是否存储缓存

image.png

image.png

image.png

image.png


ConnectInterceptor连接拦截器

ConnectInterceptor用来创建或者获取缓存中的请求连接,其实就是三次握手。不过如果缓存池中有可用连接则不需要进行三次握手,这就是连接器中主要功能---节省我们的三次握手时间

简述一下获取连接的过程(读代码的时候跟着这个思路来做):

  1. 先检查StreamAllocation类中当前的connection是否可用,如果不可用则关闭它的socket
  2. 尝试从连接池中获取连接赋值给StreamAllocation.connection,如果获取成功了则直接复用
  3. 如果第2步中没有获取成功,并且请求是http2,则尝试再次使用route获取多路复用的连接
  4. 如果第三部仍然没有获取到连接,则创建一个连接并加入到缓存池中
  5. 如果连接是第4步中创建的,则需要进行三次握手连接后才能使用
  6. 如果我们第5步中连接创建完成后,发现已经有其它请求创建了连接,则我们将我们自己创建的连接干掉,然后使用其它请求创建的连接进行返回。
  7. intercept代码

image.png

  1. newStream方法

image.png

  1. findHealthyConnection循环获取可用连接

image.png

  1. 最后一步findConnection真实的获取连接的逻辑,方法非常长

findConnection会有两次尝试从连接池中获取连接,第一次是不通过route去获取。而第二次是通过route去获取。

会有第二次获取的原因是http2有了多路复用 的概念,每个连接可以有多个stream的

http2多路复用就是同一个tcp链接可以并发执行多个http请求。开发过程中我还没见过http2呢,所以这里也不用太过深究

image.png

image.png

image.png

image.png


CallServerInterceptor请求网络拦截器

intercept方法

image.png

image.png

image.png

image.png

总结


Okhttp可以使用同步和异步请求两种方式请求网络来获取数据,请求的过程都是构建请求参数,构建Call对象,然后同步或者异步获取返回数据。

同步和异步请求的方式最终都会将Call调度到Dispatcher类中,

它们的不同之处在于:

同步请求方式会把Call加入到同步请求队列中然后执行请求。

而异步请求会可能会把请求放到正在执行中的异步请求队列中,也可能加入到异步请求准备队列中。如果我们当前正在执行的异步请求数量大于最大并发数,则会把请求加入到准备队列中。每一个异步请求执行完成后都会尝试把准备队列中的请求加入到正在执行的队列中。

需要说明的是异步请求使用线程池执行

两种方式最终都会调用getResponseWithInterceptorChain拿到最终的数据。

getResponseWithInterceptorChain里面维护了一个责任链。这个责任链里面有多个连接器,包括1重试拦截器、2桥拦截器、3缓存拦截器、4连接拦截器、5网络请求拦截器。我们的请求参数会经过这些拦截器的处理,过程就是从1->5,返回Response结果的时候会再从5->1。

实际上还包括自定义拦截器

后面的过程就是将数据返回给调用处了,同步请求直接返回结果,异步请求使用回调的方法回调结果。

拦截器RealInterceptorChain为什么不使用遍历的方式执行

  1. 遍历的方式不容易处理中断和异常的情况
  2. 我们的责任链执行有两个方向,正向执行的时候将请求参数向前传递处理。反向的时候将response回传。这一点遍历的方式是做不到的
  3. 重试的机制,比如我们的责任链每向前一次都会new一个RealInterceptorChain,这样如果第一次执行后发现请求失败了,我们想重试就会重新从RetryAndFollowUpInterceptor位置new一个RealInterceptorChain进行第二次执行。这样第一次请求新建的几个责任链单元的缓存数据不会影响我们第二次执行。如果使用遍历的方式就不可能达到这个效果。

例如当你使用遍历的方式实现责任链,想重试的时候当请求走得到CacheInterceptor,发现CacheInterceptor中包含了我们上一次执行的逻辑残留,他就需要处理付出更多的逻辑处理这种逻辑残留,无形中增加了工作量。



相关文章
|
8月前
|
开发工具 Android开发 git
Windows下载android2.2完整源码(转)
Windows下载android2.2完整源码(转)
101 3
|
7月前
|
存储 缓存 Android开发
安卓Jetpack Compose+Kotlin, 使用ExoPlayer播放多个【远程url】音频,搭配Okhttp库进行下载和缓存,播放完随机播放下一首
这是一个Kotlin项目,使用Jetpack Compose和ExoPlayer框架开发Android应用,功能是播放远程URL音频列表。应用会检查本地缓存,如果文件存在且大小与远程文件一致则使用缓存,否则下载文件并播放。播放完成后或遇到异常,会随机播放下一首音频,并在播放前随机设置播放速度(0.9到1.2倍速)。代码包括ViewModel,负责音频管理和播放逻辑,以及UI层,包含播放和停止按钮。
|
5月前
|
Ubuntu 开发工具 Android开发
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
本文介绍了在基于Ubuntu 22.04的环境下配置Python 3.9、安装repo工具、下载和同步AOSP源码包以及处理repo同步错误的详细步骤。
295 0
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
|
8月前
|
安全 Android开发
Android之OKHttp基本使用和OKHttp发送https请求安全认证
Android之OKHttp基本使用和OKHttp发送https请求安全认证
281 0
|
5月前
|
开发工具 git 索引
repo sync 更新源码 android-12.0.0_r34, fatal: 不能重置索引文件至版本 ‘v2.27^0‘。
本文描述了在更新AOSP 12源码时遇到的repo同步错误,并提供了通过手动git pull更新repo工具来解决这一问题的方法。
189 1
|
5月前
|
Android开发 Docker 容器
docker中编译android aosp源码,出现Build sandboxing disabled due to nsjail error
在使用Docker编译Android AOSP源码时,如果遇到"Build sandboxing disabled due to nsjail error"的错误,可以通过在docker run命令中添加`--privileged`参数来解决权限不足的问题。
1020 1
|
5月前
|
API 开发工具 Android开发
Android源码下载
Android源码下载
604 0
|
5月前
|
开发工具 uml git
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
665 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
|
5月前
|
Java Android开发 芯片
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
243 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
|
5月前
|
Android开发
我的Android 进阶修炼(1): AOSP源码根目录结构
本文介绍了AOSP源码的根目录结构,提供了基于MTK9269 Android 9.0源码的目录说明,帮助读者了解AOSP源码的组织方式和各目录的功能。
268 0
我的Android 进阶修炼(1): AOSP源码根目录结构