OkHttp分析之同步请求

Android2023年9月11日 am8:08发布11个月前更新 3XCN.COM站长
0 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.91es.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、 本站名称: 91易搜
2、 本站网址: 91es.com3xcn.com
3、 本站文章: 部分来源于网络,仅供站长学习和参考,若侵权请留言
广告也精彩

相关文章

广告也精彩

暂无评论

评论审核已启用。您的评论可能需要一段时间后才能被显示。

暂无评论...

网站升级中

公告

近期网站升级中,可能存在一些bug。欢迎反馈 https://www.91es.com/we.html

本站域名

本站域名 : 91es.com3xcn.com。本站邮箱 : 站长邮箱 i@oorr.cn,通知邮箱we@oorr.cn ,如有更新,请看公告 。