OkHttp分析之同步请求

Android  2023年9月11日 am8:08发布1年前 (2023)更新 城堡大人
99 0 0

前言

接上文网络请求框架OkHttp的同步异步简单使用介绍《OkHttp简单介绍之一》,今天就对同步请求的源码分析一下,加深一下印象。记录一下,方便自己查阅。

正文

这里用的框架版本

implementation 'com.squareup.okhttp3:okhttp:3.12.0'

有新版本,我没有升级,暂时就以这个记录。

下面是同步代码

//创建OkHttpClient
OkHttpClient client = new OkHttpClient.Builder().build();
//创建Request
Request request = new Request.Builder().get().url("https://www.biumall.com/").build();
//创建Call,其实也是对request的封装
Call call = client.newCall(request);
//同步请求
Response response = call.execute();

OkHttpClient

OkHttpClient的创建是用了Builder模式。进入Builder()

Builder()
public Builder() {
  dispatcher = new Dispatcher();
  protocols = DEFAULT_PROTOCOLS;
  connectionSpecs = DEFAULT_CONNECTION_SPECS;
  eventListenerFactory = EventListener.factory(EventListener.NONE);
  proxySelector = ProxySelector.getDefault();
  if (proxySelector == null) {
    proxySelector = new NullProxySelector();
  }
  cookieJar = CookieJar.NO_COOKIES;
  socketFactory = SocketFactory.getDefault();
  hostnameVerifier = OkHostnameVerifier.INSTANCE;
  certificatePinner = CertificatePinner.DEFAULT;
  proxyAuthenticator = Authenticator.NONE;
  authenticator = Authenticator.NONE;
  connectionPool = new ConnectionPool();
  dns = Dns.SYSTEM;
  followSslRedirects = true;
  followRedirects = true;
  retryOnConnectionFailure = true;
  callTimeout = 0;
  connectTimeout = 10_000;
  readTimeout = 10_000;
  writeTimeout = 10_000;
  pingInterval = 0;
}

也就是参数的初始化

Request

也是用了Builder模式,初始化请求方式和添加url。

Builder()
public Builder() {
  this.method = "GET";
  this.headers = new Headers.Builder();
}

请求方式可以用默认的,也可以进行修改。

public Builder get() {
  return method("GET", null);
}
public Builder post(RequestBody body) {
  return method("POST", body);
}
public Builder delete(@Nullable RequestBody body) {
  return method("DELETE", body);
}
public Builder put(RequestBody body) {
  return method("PUT", body);
}
public Builder patch(RequestBody body) {
  return method("PATCH", body);
}
url()

传入请求url

public Builder url(String url) {
  if (url == null) throw new NullPointerException("url == null");
  if (url.regionMatches(true, 0, "ws:", 0, 3)) {
    url = "http:" + url.substring(3);
  } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
    url = "https:" + url.substring(4);
  }
  return url(HttpUrl.get(url));
}

进行null判断和一定规则转换

public Builder url(HttpUrl url) {
  if (url == null) throw new NullPointerException("url == null");
  this.url = url;
  return this;
}

最终传入到Request的url中。

Call

# OkHttpClient.java
@Override 
public Call newCall(Request request) {
  return RealCall.newRealCall(this, request, false);
}

真正调用的是RealCall的newRealCall()

newRealCall()
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
  //创建RealCall
  RealCall call = new RealCall(client, originalRequest, forWebSocket);
  //初始化eventListener
  call.eventListener = client.eventListenerFactory().create(call);
  return call;
}

这里创建了RealCall对象

RealCall
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
  this.client = client;
  this.originalRequest = originalRequest;
  this.forWebSocket = forWebSocket;
  this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  this.timeout = new AsyncTimeout() {
    @Override protected void timedOut() {
      cancel();
    }
  };
  this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
}
execute()

同步请求。Call中没有实现,在RealCall中实现了,而且上面创建并返回的就是RealCall对象。

# RealCall.java
@Override public Response execute() throws IOException {
  synchronized (this) {
    //是否执行过标志,如果已经请求过,就不能再次请求了
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  captureCallStackTrace();
  timeout.enter();
  eventListener.callStart(this);
  try {
    //同步请求
    client.dispatcher().executed(this);
    //获取Response
    Response result = getResponseWithInterceptorChain();
    if (result == null) throw new IOException("Canceled");
    return result;
  } catch (IOException e) {
    e = timeoutExit(e);
    eventListener.callFailed(this, e);
    throw e;
  } finally {
    //通知请求结束
    client.dispatcher().finished(this);
  }
}

上面有备注,我们看重点

client.dispatcher().executed(this);
executed()

调用的是Dispatcher的,很简单,也就是添加到同步任务列表中。

synchronized void executed(RealCall call) {
  runningSyncCalls.add(call);
}
getResponseWithInterceptorChain()

拦截链创建,这次才是真正的网络请求。

Response getResponseWithInterceptorChain() throws IOException {
  // 创建需要的拦截器,并添加到interceptors
  List<Interceptor> interceptors = new ArrayList<>();
  interceptors.addAll(client.interceptors());
  interceptors.add(retryAndFollowUpInterceptor);
  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));
  //创建RealInterceptorChain拦截器链
  //也就是把上面拦截器进行链接在一起,一级一级的调用,返回Response
  Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
      originalRequest, this, eventListener, client.connectTimeoutMillis(),
      client.readTimeoutMillis(), client.writeTimeoutMillis());
  return chain.proceed(originalRequest);
}

下面是主要的拦截器

RetryAndFollowUpInterceptor
BridgeInterceptor
CacheInterceptor
ConnectInterceptor
CallServerInterceptor

进入proceed,这里是会把所有的拦截器串在一起。

# RealInterceptorChain.java
@Override public Response proceed(Request request) throws IOException {
  return proceed(request, streamAllocation, httpCodec, connection);
}

真正处理是这里

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
    RealConnection connection) throws IOException {
  //略
  //[重点],获取下一个拦截器链(index + 1),也就是第一个不处理
  RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
      connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
      writeTimeout);
  //获取list中的拦截器
  Interceptor interceptor = interceptors.get(index);
  //[重点]调用拦截器的intercept()
  Response response = interceptor.intercept(next);
  //略
  return response;
}

关注

 Response response = interceptor.intercept(next);

interceptor就是我们上面存在interceptors中的拦截器。第一个不处理,也就是上面访问第二个RetryAndFollowUpInterceptor。

因此上面等于

RetryAndFollowUpInterceptor.intercept(next)

这里只是简单介绍,细节不介绍哈,目前关注就是如何串在一起的。

# RetryAndFollowUpInterceptor.java
@Override 
public Response intercept(Chain chain) throws IOException {
  while (true) {
    //略
    Response response;
    boolean releaseConnection = true;
    try {
      //[重点]又调用了proceed(),是不是又回到上面了,index+1
      //response是下一个拦截器返回的。
      response = realChain.proceed(request, streamAllocation, null, null);
      releaseConnection = false;
    } catch (RouteException e) {
    } catch (IOException e) {
    } finally {
    }
  }
}

proceed()中的index+1,那就是再一次执行下一个拦截器。

类似于递归,只是每次调用的拦截器不一样,直到遍历完interceptors。

finished

当任务结束时就移除

void finished(RealCall call) {
  finished(runningSyncCalls, call);
}
private <T> void finished(Deque<T> calls, T call) {
  Runnable idleCallback;
  synchronized (this) {
    if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
    idleCallback = this.idleCallback;
  }
  boolean isRunning = promoteAndExecute();
  if (!isRunning && idleCallback != null) {
    idleCallback.run();
  }
}

参考文章

  1. okhttp

 历史上的今天

  1. 2024: [摘]Android启动时间优化(0条评论)
  2. 2024: Kotlin的基本语法(0条评论)
  3. 2022: Socket的简单使用记录(0条评论)
  4. 2020: Android监听状态栏的显示与隐藏状态栏(0条评论)
  5. 2019: 梁文道:在铁路上开餐(0条评论)
版权声明 1、 本站名称: 笔友城堡
2、 本站网址: https://www.biumall.com/
3、 本站部分文章来源于网络,仅供学习与参考,如有侵权,请留言

暂无评论

暂无评论...

随机推荐

世界,您好!

1、好记性不如烂笔头;2、觉得已经晚了,恰恰是最早的时候;3、鸡蛋从外打破是食物,从内打破是生命我们新版上线啦(掌声),这次改版很大,放弃之前传统的导航模式,首先采用“网址导航+读书(阅读)+博客”全新的模式,依旧是国内第一个以读书(阅读)为主题的网址导航大全!因为热爱,所以折腾!P...

林徽因:一片阳光

放了假,春初的日子松弛下来。将午未午时候的阳光,澄黄的一片,由窗棂横浸到室内,晶莹地四处射。我有点发怔,习惯地在沉寂中惊讶我的周围。我望着太阳那湛明的体质,像要辨别它那交织绚烂的色泽,追逐它那不着痕迹的流动。看它洁净地映到书桌上时,我感到桌面上平铺着一种恬静,一种精神上的豪兴,情趣上的闲逸;即或所谓...

Kotlin面对对象简介

前言简单介绍一下Kotlin面对对象简介。记录一下,方便自己查阅。正文类类就是具备某些共同特征的实体的集合,它是一种抽象的数据类型,它是对所具有相同特征实体的抽象。Kotlin中所有类都继承Any类,它是所有类的父类,如果一个类在声明时没有指定父类,则默认父类为Any类无参数和有参...

Can't determine type for tag

前言引入公共库库(commonLib)时,出现如下异常,说实话,看不懂哈,就问谷歌了。Can't determine type for tag '<macro name="m3_comp_assist_chip_container_shape">?attr/shapeAppeara...

王蒙:永远的巴金

在这个星空之夜,巴金走了。如果设想一下近百年来最受欢迎和影响最大的一部长篇小说,我想应该是巴金的《家》。早在小时候,我的母亲与姨母就在议论鸣凤和觉慧,梅表姐和琴,觉新觉民高老太爷和老不死的冯乐山,且议且叹,如数家珍。而等到我自己迷于阅读的时候,我宁愿读《灭亡》和《新生》,因为这两本书里写了革命,...

海子:七月不远

七月不远性别的诞生不远爱情不远————马鼻子下湖泊含盐因此青海湖不远湖畔一捆捆蜂箱使我显得凄凄迷人青草开满鲜花。青海湖上我的孤独如天堂的马匹(因此 天堂的马匹不远)我就是那个情种:诗中吟唱的野花天堂的马肚子里唯一含毒的野花(青海湖 请熄灭我的爱情!)野花青梗不远医...