OkHttp相关知识(二)
创始人
2024-01-25 15:18:18
0

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 readyAsyncCalls = new ArrayDeque<>();/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */private final Deque runningAsyncCalls = new ArrayDeque<>();/** Running synchronous calls. Includes canceled calls that haven't finished yet. */private final Deque 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 executableCalls = new ArrayList<>();boolean isRunning;synchronized (this) {for (Iterator 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 :正在等待执行,等待缓存的队列

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

  • Dispatcher 生产者

  • ExecutorService:消费者池

OkHttp的任务调度

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

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

private boolean promoteAndExecute() {assert (!Thread.holdsLock(this));List executableCalls = new ArrayList<>();boolean isRunning;synchronized (this) {for (Iterator 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 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进行处理,返回给上一个拦截器

相关内容

热门资讯

中小板和创业板哪个好,创业板资...   写作缘由      上周,一个朋友来公司谈话,长期投资创业板50指数或科创板50指数是不是一个比...
未来健康行业发展趋势,健康食品...   研究表明,虽然个人健康仍然是购买食品的主要驱动力,但由于环境原因,消费者正在转向购买有机食品,这...
学校申请请假回家自学怎么写,大...         管理者在工作中应该有所为、有所不为。      同时,小仙的部门里还有两个性格很强的...
淘宝衣服店铺名字大全 淘宝衣服...   家庭装修,有很多东西其实可以在网上购买。只要不需要量确切的尺寸,不怕敲、易碎、易退的东西都可以在...
创业父母不给一分钱,拿父母钱去...   创业者是很孤独的。      作为一个电商老板,我经常约一些朋友喝酒,因为大家都是孤独的!我为什...
创业失败经历,为什么第一次创业...   #为什么在农村创业这么难#      为什么在农村创业这么难?      我不知道有多少人在农村...
陕西创业示范园区,陕西农村创业...   9月2日,由黄牛铺镇力推、县工商局评选的黄牛铺镇长潭坝村凤县鑫鑫五味子产业园项目获得陕西省第六届...
黄轩对宋轶评价,黄轩宋轶《创业...   文/马清运      到目前为止,电视剧《创业时代》已经播出了9集,基本上所有的主要角色都出现了...
创业板半年报业绩预增公告时间,...         本文首发于同名公众号      每年的7、8月份是a股半年报的发布日,可能有利于刺激...
2人合作开店怎么分钱,创业公司...   关注@ meme创业,分享金融投资,挖掘创业陷阱,助你登上人生巅峰!      当当。com的一...