Okhttp 01 核心流程详解

1.dns解析,域名对应 ip
2.TCP建立连接,三次握手
3.C端向S端发送请求行命令
4.C端发送请求头信息
5.S端应答,发送响应命令
6.S端发送响应头信息
7.S端向C端发送数据,以及消息体
8.S端关闭链接 tcp 四次挥手

An HTTP client for Android, Kotlin, and Java.

https://square.github.io/okhttp/
源码
https://github.com/square/okhttp

参考:
hencoder plus
https://juejin.im/post/5d6e7f256fb9a06ae57d0d9c
4.x
OkHttp works on Android 5.0+ (API level 21+) and on Java 8+.

The OkHttp 3.12.x branch supports Android 2.3+ (API level 9+) and Java 7+.
These platforms lack support for TLS 1.2 and should not be used.
But because upgrading is difficult we will backport critical fixes to the 3.12.x branch through December 31, 2020.

一个简单请求 okhttp 做了什么

  1. 创建⼀个 OkHttp 的实例
    OkHttpClient client = new OkHttpClient.Builder().build();

  2. 创建 Request
    Request request = new Request.Builder()
    .url(“http://hencoder.com”)
    .build();

  3. 创建 Call 并发起⽹络请求

Call call = client.newCall(request);
@Override public Call newCall(Request request) {
  return RealCall.newRealCall(this, request, false /* for web socket */);
}
client.newCall(request).enqueue(new Callback() {

@Override 
public void onFailure(Call call, IOException e) {}

@Override public void onResponse(Call call, Response response) throws IOException{
Log.d("okhttp response", response.body().string());}
});

可以看到,使用okhttp发起网络请求要经过4步:

  1. 创建OkHttpClient
  2. 创建请求Request
  3. 通过OkHttpClient和Request创建一个Call,用于发起网络请求
  4. 调用Call的execute()或enqueue()方法发起同步或异步请求

本次分析的okhttp源码版本是基本3.14.x

小结

okhttp的异步请求过程分析总结

  • 当我们调用call.enqueue(Callback)时,就会发起一个异步请求,
  • 实际执行的是realCall.enqueue(Callback),它比同步请求只是多了一个Callback参数,
  • 然后realCall.execute()中先把传进来的Callback包装成一个AsyncCall,
  • 然后执行Dispatcher的enqueue(AsyncCall)把这个异步请求任务保存进readyAsyncCalls队列中,
  • 保存后开始执行 promoteAndExecute()进行异步任务的调度,
  • 它会先把符合条件的异步请求任务从readyAsyncCalls转移到runningAsyncCalls队列和添加到executableCalls列表中去,
  • 然后遍历executableCalls列表,逐个执行AsyncCall 的executeOn(ExecutorService),
  • 然后在这个方法中AsyncCall会把自己放进Dispatcher 的线程池,等待线程池的调度
  • 当线程池执行到这个AsyncCall时,它的run方法就会被执行,从而执行重写的execute()方法,execute()方法中的流程和同步请求流程大致相同。

下面是异步请求过程的调用链:

整个流程

  1. okhttp通过Builder模式创建OkHttpClient、Request和Response,
  2. 通过client.newCall(Resquest)创建一个Call,用于发起异步或同步请求
  3. 请求会经过Dispatcher、一系列拦截器,最后通过okio与服务器建立连接、发送数据并解析返回结果
    这个过程如图:

1、创建OkHttpClient

OkHttpClient是okhttp中的大管家,它将具体的工作分发到各个子系统中去完成,它使用Builder模式配置网络请求的各种参数如超时、拦截器、分发器等,Builder中可配置的参数如下:
builder 模式是,先赋值正确,在创建对象。set 是先给予默认值,然后修改。

//OkHttpClient.Builder
public static final class Builder {
    Dispatcher dispatcher;//分发器
    @Nullable Proxy proxy;//代理
    List<Protocol> protocols;//应用层协议
    List<ConnectionSpec> connectionSpecs;//传输层协议
    final List<Interceptor> interceptors = new ArrayList<>();//应用拦截器
    final List<Interceptor> networkInterceptors = new ArrayList<>();//网络拦截器
    EventListener.Factory eventListenerFactory;//http请求回调监听
    ProxySelector proxySelector;//代理选择
    CookieJar cookieJar;//cookie
    @Nullable Cache cache;//网络缓存
    @Nullable InternalCache internalCache;//内部缓存
    SocketFactory socketFactory;//socket 工厂
    @Nullable SSLSocketFactory sslSocketFactory;//安全套接层socket 工厂,用于HTTPS
    @Nullable CertificateChainCleaner certificateChainCleaner;//验证确认响应证书,适用 HTTPS 请求连接的主机名
    HostnameVerifier hostnameVerifier;//主机名字确认
    CertificatePinner certificatePinner;//证书链
    Authenticator proxyAuthenticator;//代理身份验证
    Authenticator authenticator;//本地身份验证
    ConnectionPool connectionPool;//连接池,复用连接
    Dns dns;//域名
    boolean followSslRedirects;//安全套接层重定向
    boolean followRedirects;//本地重定向
    boolean retryOnConnectionFailure;//错误重连
    int callTimeout;//请求超时,它包括dns解析、connect、read、write和服务器处理的时间
    int connectTimeout;//connect超时
    int readTimeout;//read超时
    int writeTimeout;//write超时
    int pingInterval;//ping超时

    //这里是配置默认的参数
    public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;//Protocol.HTTP_2和Protocol.HTTP_1_1
      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;
    }

    //这里通过另外一个OkHttpClient配置参数
    Builder(OkHttpClient okHttpClient) {
      this.dispatcher = okHttpClient.dispatcher;
      this.proxy = okHttpClient.proxy;
      this.protocols = okHttpClient.protocols;
      //...
    }
    
    //...
    
    //配置完参数后,通过Builder的参数创建一个OkHttpClient
    public OkHttpClient build() {
        return new OkHttpClient(this);
    }
}

2、创建请求Request

在 okhttp 中 Request 代表着一个HTTP请求,它封装了请求的具体消息,如url、header、body等,它和OkHttpClient一样都是使用Budiler模式来配置自己的参数,如下:

//Request.Budiler
public static class Builder {
    HttpUrl url;
    String method;
    Headers.Builder headers;
    RequestBody body;

    //这里配置默认的参数
    public Builder() {
      this.method = "GET";//默认是GET请求
      this.headers = new Headers.Builder();
    }

    //这里通过另外一个Request配置参数
    Builder(Request request) {
      this.url = request.url;
      this.method = request.method;
      //...
    }
    
    //...
    
    //配置完参数后,通过Builder的参数创建一个Request
    public Request build() {
        if (url == null) throw new IllegalStateException("url == null");
        return new Request(this);
    }
}

3、创建用于发起网络请求的Call

Call是一个接口,它的具体实现类是RealCall,Call中定义了一些enqueue(Callback)、execute()等关键方法,如下:
调用**client.newCall(request)**其实返回的是RealCall对象,而RealCall封装了请求的调用逻辑。

public interface Call extends Cloneable {
    //返回当前请求
    Request request();
    //同步请求方法,此方法会阻塞当前线程直到请求结果放回
    Response execute() throws IOException;
    //异步请求方法,此方法会将请求添加到队列中,然后等待请求返回
    void enqueue(Callback responseCallback);
    //取消请求
    void cancel();
	//判断请求是否在执行
    boolean isExecuted();
    //判断请求是否取消
    boolean isCanceled();
    //返回请求的超时时间
    Timeout timeout();
    //克隆一个新的请求
    Call clone();
    interface Factory {
        Call newCall(Request request);
    }
}


public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
    //...
    @Override 
    public Call newCall(Request request) {
        //调用了RealCall的newRealCall()
        return RealCall.newRealCall(this, request, false /* for web socket */);
    }
}

final class RealCall implements Call {
    //...
    static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) 	{
        //返回RealCall对象
        RealCall call = new RealCall(client, originalRequest, forWebSocket);
        call.transmitter = new Transmitter(client, call);
        return call;
    }
}



同步请求 - RealCall :: execute()
//RealCall.java
@Override
public Response execute() throws IOException {
    //...
    try {
        //1、调用Dispatcher的executed(RealCall)方法
        client.dispatcher().executed(this);
        //2、调用getResponseWithInterceptorChain()方法
        return getResponseWithInterceptorChain();
    } finally {
        //3、同步请求任务执行完毕,调用Dispatcher的finished(RealCall)方法
        client.dispatcher().finished(this);
    }
}
client就是我们上面所讲的OkHttpClient的实例,它在创建RealCall时作为构造参数传了进去,而OkHttpClient的dispatcher()方法返回的是Dispatcher实例,
它在OkHttpClient构造时被创建。
Dispatcher是一个任务调度器
它负责进行请求任务的调度,它的内部维护着3个任务队列(readyAsyncCalls、runningAsyncCalls、runningSyncCalls)1个线程池(executorService),Dispatcher主要内容如下:
public final class Dispatcher {
    private int maxRequests = 64;//最大请求数64个
    private int maxRequestsPerHost = 5;//每个主机最大请求数5个
    private @Nullable Runnable idleCallback;//idle任务回调,类似于Android的idlehandler, 可以在Dispatcher没有任务调度(空闲时)时执行idleCallback中的任务

    //线程池,执行runningAsyncCalls队列里面的请求任务
    private @Nullable ExecutorService executorService;

    //等待执行的异步请求任务队列
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

    //正在执行的异步请求任务队列
    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

    //正在执行的同步请求任务队列
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

    synchronized void executed(RealCall call) {
        //...
    }

    void enqueue(AsyncCall call) {
        //...
    }  
    
    void finished(RealCall call) {
        //...
    }

    void finished(AsyncCall call) {
        //...
    }
    
    private boolean promoteAndExecute() {
        //...
    }

  //...  
}
  • Dispatcher提供了
    executed(RealCall)和enqueue(AsyncCall)方法来进行同步和异步请求任务的入队,
  • 还提供了finished(RealCall)和finished(AsyncCalll)方法来进行同步和异步请求任务的出队
  //Dispatcher
  /** Ready async calls in the order they'll be run. */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

可以看到okhttp把ReadCall当作同步请求任务的代表,把AsyncCall当作异步请求任务的代表,

  1. RealCall前面已经讲过了,而AsyncCal是RealCall的一个内部类,它本质上就是一个Runnable,
  2. Dispatcher的线程池执行任务主要执行的是 runningAsyncCalls 队列里面的异步请求任务,
    也就是AsyncCall异步任务
  3. 而Dispatcher的promoteAndExecute()方法就是用来进行异步任务的调度,它的逻辑主要是按顺序把readyAsyncCalls队列中准备执行的异步任务转移到runningAsyncCalls后,
    再由线程池执行,对于同步任务 Dispatcher只是暂时保存在runningSyncCalls队列中,并不会由线程池执行。

同步执行:

1. Dispatcher :: executed(RealCall)

看RealCall的execute()方法,
它首先调用了Dispatcher的executed(RealCall)方法
,Dispatcher的executed(RealCall)方法实现如下:

//Dispatcher.java
synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
}

可以看到没有做什么处理,只是简单的把同步请求任务放入runningSyncCalls队列。

2. RealCall :: getResponseWithInterceptorChain()

看RealCall的execute(),调用getResponseWithInterceptorChain()方法,这里才是同步请求处理的地方,我们点进去,如下:


//RealCall.java 
Response getResponseWithInterceptorChain() throws IOException {
    //拦截器的添加
    List<Interceptor> 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());
    }
    //添加的最后一个拦截器是CallServerInterceptor
    interceptors.add(new CallServerInterceptor(forWebSocket));

    //创建一个RealInterceptorChain,传入了interceptors和Request
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    try {
      //调用RealInterceptorChain的proceed(Request)方法处理请求
      Response response = chain.proceed(originalRequest);
      //...
      return response;
    } catch (IOException e) {
     //...
    } finally {
     //...
    }
  }

3. Dispatcher :: finished(RealCall)

我们继续看RealCall的execute()方法,调用Dispatcher的finished(AsyncCall)方法,如下:

//Dispatcher.java
void finished(RealCall call) {
    //传进了runningSyncCalls队列
    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;
    }

    //紧接着调用promoteAndExecute()方法进行异步任务的调度,如果没有异步任务要进行,promoteAndExecute()返回false
    boolean isRunning = promoteAndExecute();

    //isRunning等于false且设置了idleCallback,会执行一遍idle任务
    if (!isRunning && idleCallback != null) {
        idleCallback.run();
    }
}
  • finished()方法中首先尝试从runningSyncCalls队列把刚才通过 executed()入队的同步任务RealCall移除
  • 如果移除失败,就抛出异常
  • 如果移除成功,就紧接着调用promoteAndExecute()方法进行异步任务的调度并尝试执行一遍idle任务,promoteAndExecute()方法在异步请求中再详细介绍。

小结

至此okhttp的同步请求过程分析完毕,这里总结一下:

  1. 当我们调用call.execute()时,就会发起一个同步请求,
  2. 而call的实现类是RealCall,所以实际执行的是realCall.execute(),
  3. realCall.execute()中执行Dispatcher的executed(RealCall)把这个同步请求任务保存进runningSyncCalls队列中,(没有做事情,只是入队出队。)
  4. 然后RealCall执行getResponseWithInterceptorChain()处理同步请求,
  5. 请求经过层层拦截器后到达最后一个拦截器CallServerInterceptor,在这个拦截器中通过Exchange把请求发送到服务器,
  6. 然后同样的通过Exchange获得服务器的响应,根据响应构造Response,然后返回,
  7. 最后RealCall执行Dispatcher的finished(RealCall)把之前暂时保存的同步请求任务从runningSyncCalls队列中移除。

下面是同步请求过程的调用链:

异步请求 - RealCall.enqueue(Callback)

//RealCall.java
@Override
public void enqueue(Callback responseCallback) {
    //...
    //1、调用Dispatcher的enqueue(AsyncCall)方法
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

它比同步请求只是多了一个Callback,

在Callback的 onResponse(Call, Response)回调中我们可以拿到网络响应返回的Response,
RealCall的enqueue(Callback)方法中首先把Callback用AsyncCall包装起来,
然后调用调用Dispatcher的enqueue(AsyncCall)方法。

1. Dispatcher :: enqueue(AsyncCall)

我们看Dispatcher的enqueue(AsyncCall)方法,如下:

//Dispatcher.java
void enqueue(AsyncCall call) {
    synchronized (this) {
       readyAsyncCalls.add(call);
       //...
    }
    promoteAndExecute();
}

2. Dispatcher :: promoteAndExecute()

//Dispatcher.java
private boolean promoteAndExecute() {
    //准备一个正在执行任务列表executableCalls
    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
        //1、这个for循环主要把readyAsyncCalls中等待执行的异步任务转移到runningAsyncCalls队列和executableCalls列表中去
        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {

            //取出readyAsyncCalls中等待执行的异步任务
            AsyncCall asyncCall = i.next();

            //判断条件:1、正在运行的异步请求任务不能大于maxRequests;2、等待执行的异步任务的主机请求数不能大于maxRequestsPerHost
            if (runningAsyncCalls.size() >= maxRequests) break; 
            if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue;
            //满足条件,进入下面逻辑

            //把这个等待执行的异步任务从readyAsyncCalls中移除
            i.remove();
            asyncCall.callsPerHost().incrementAndGet();
            
            //把这个等待执行的异步任务添加进executableCalls列表
            executableCalls.add(asyncCall);
            //把这个等待执行的异步任务添加进runningAsyncCalls队列
            runningAsyncCalls.add(asyncCall);
        }
        
        //runningCallsCount()里面的逻辑: return runningAsyncCalls.size() + runningSyncCalls.size();
        isRunning = runningCallsCount() > 0;
    }
    //2、这个for循环主要是执行executableCalls列表中的异步任务
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
        AsyncCall asyncCall = executableCalls.get(i);
        //传进executorService,调用AsyncCall的executeOn()方法,由线程池执行这个异步任务
        asyncCall.executeOn(executorService());
    }

    return isRunning;
}


promoteAndExecute()方法中主要是2个for循环,

  • 第 1 个for循环是把符合条件的异步请求任务从readyAsyncCalls转移(提升)到runningAsyncCalls队列和添加到executableCalls列表中去,
  • 第 2 个for循环就是遍历executableCalls列表,从executableCalls列表中获取AsyncCall对象,并且调用它的executeOn()方法,
  • executeOn()方法传进了一个Dispatcher的executorService,所以我们看AsyncCall的executeOn()方法,里面是真正执行异步请求任务的地方。

3. 1、AsyncCall :: executeOn(ExecutorService)

AsyncCall的executeOn()方法如下:

//AsyncCall.java
void executeOn(ExecutorService executorService) {
    boolean success = false;
    try {
        //传进this,执行AsyncCall异步任务,AsyncCall本质是Runnable
        executorService.execute(this);
        success = true;
    } catch (RejectedExecutionException e) {
       //...
    } finally {
        if (!success) {
            //异步任务执行失败,调用Dispatcher的finished(AsyncCall)方法
            client.dispatcher().finished(this); 
        }
    }

主要逻辑就是调用
executorService.execute(this)执行当前的 AsyncCall 异步任务,
AsyncCall实现了NamedRunnable,本质是Runnable,如下:

final class AsyncCall extends NamedRunnable {
    //...
}

public abstract class NamedRunnable implements Runnable {
	//...	
  @Override
    public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      //run方法中执行execute()方法
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

AsyncCall实现了NamedRunnable,所以AsyncCall重写了execute()实现了执行逻辑,所以我们直接看AsyncCal的execute()方法。

4. 2、AsyncCall :: execute()

AsyncCal的execute()方法如下:
//AsyncCall.java
@Override 
protected void execute() {
    //...
    try {
        //调用RealCall的getResponseWithInterceptorChain()方法处理请求
        Response response = getResponseWithInterceptorChain();
        signalledCallback = true;
        //请求处理完毕,返回响应,回调Callback的onResponse()方法
        responseCallback.onResponse(RealCall.this, response);
    } catch (IOException e) {
        //...
    } finally {
        //异步请求任务执行完毕,调用Dispatcher的finished(AsyncCall)方法
        client.dispatcher().finished(this);
    }
}

AsyncCal的execute()方法的逻辑和前面介绍的同步请求过程殊途同归,

  1. 首先调用RealCall的getResponseWithInterceptorChain()方法处理请求
  2. 请求处理完毕后,返回响应Response,
  3. 这时回调我们调用Call.enqueue(Callback)时传进来的Callback的onResponse()方法,
  4. 最后在finally语句中调用Dispatcher的finished(AsyncCall)方法来把异步请求任务从runningAsyncCalls队列中移除出去,
  5. 这个移除逻辑和上面同步请求任务的移除逻辑一样,只是这次是从runningAsyncCalls移除而不是runningSyncCalls.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值