OkHttp 源码分析 - Okhttp同步请求、异步请求过程

浪人 等级 1327 0 0

  RxJava源码的基础部分分析的差不多,后续如果有新的内容话,会继续的补充。从今天开始,我们来看看OkHttp的相关源码。OkHttp的源码过于复杂,涉及到的方面非常的多,本系列文章目的是打通Okhttp的整个执行流程,不对某一个细节重点分析。
  本篇文章是本系列文章的第一篇,我们先从最简单的Okhttp使用入手,进而分析Okhttp两种请求方式的流程。

1. 同步请求

  同步请求的重点在于同步二字,顾名思义,执行同步请求,不会单独的开一个线程,所以在进行网络请求时,当前线程会阻塞在这里。
  我们来简单的看看,怎么进行一个同步请求:

 private final OkHttpClient mOkHttpClient = new OkHttpClient.Builder().readTimeout(50, TimeUnit.SECONDS).build();

  private void syncRequest() {
    Request request = new Request.Builder().url("http://www.baidu.com").get().build();
    Call call = mOkHttpClient.newCall(request);
    try {
      Response response = call.execute();
    } catch (IOException e) {
      e.printStackTrace();
    }
  } 

  同步请求的执行是调用了callexecute方法。但是在调用execute方法之前,还要进行一些准备操作。

(1). OkHttpClient的创建

  OkHttpClient是一个非常基础的类,在使用OkHttp来进行网络请求时,我们必须先创建OkHttpClient的对象。在这个类里面,我们会配置很多的参数,比如超时连接时间、拦截器等等。我们来详细的看一看:

 private final OkHttpClient mOkHttpClient = new OkHttpClient.Builder()
    .readTimeout(50, TimeUnit.SECONDS)
    .build(); 

  上面就是一个非常简单的创建列子。现在我们来从源码的角度来看看OkHttpClient究竟给我们配置那些参数。
  我们知道OkHttpClient是通过建造者模式来创建的,我们先来看看OkHttpClient.Builder这个类的构造方法:

 public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      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;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    } 

  额,构造方法里面初始化我们很多的东西,这里我们需要看两个东西:

 dispatcher = new Dispatcher();
      connectionPool = new ConnectionPool(); 

   Dispatcher是一个分发器,我们所有的Request请求都是通过Dispatcher分发的。在后续的文章,我会详细讲解这个类。本文就不对它做过多的解释。
  ConnectionPool是一个连接池,很多的请求连接都由这个类管理,比如有些连接需要重用,都由这个类来管理的。这个类跟Dispatcher类一样,后续会详细的介绍这个类。
  最后就是调用Builderbuild方法来真正创建OkHttpClient对象。

(2). Request的创建

  Request的创建方式跟 OkHttpClient都是通过Builder方法来创建的,在创建Request的对象时,我们会初始化很多东西,比如请求方式(get或者post)、请求的URL等等。这里就不详细的分析了。

(3).Call的创建

  我们调用OkHttpClientnewCall方法来创建一个Call对象,我们来看看Call对象的创建过程。

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

  Call是一个接口,它的唯一实现类是RealCall类,所以我们可以看到在Call方法里面又调用了RealCallnewRealCall方法。
  我们来看看在RealCallnewRealCall方法里面进行哪些操作:

 static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }
  private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  } 

  归根结底,还是将我们创建好的OkHttpClient对象和Request对象传递到RealCall方法里面了。

(4).调用Call的execute方法

  同步请求的最后一步操作就是调用Callexecute方法,我们来看看整个execute方法时怎么执行的。

 @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  } 

  我们来重点分析几个点。首先:

 synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    } 

  从这里看的出来,每一个Call只能被执行一次,当第二次调用时,会抛出IllegalStateException异常。
  然后就是这一对:

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

  在正式进行网络请求之前,会调用Dispatcherexecuted方法来将一个Call对象放在一个队列;在正式进行网络请求之后,会调用Dispatcherfinished方法将这个Call对象从队列中移除。
  但是,这里,我们没有看到网络请求的步骤啊?究竟是哪一步进行了网络请求呢?没错,就是这一步。

 Response result = getResponseWithInterceptorChain(); 

  getResponseWithInterceptorChain方法通过调用拦截器链的每一个拦截器的proceed方法,最终返回Response对象,就是我们请求的数据。拦截器部分,在本篇文章不进行解释,后续会单独解释OkHttp的拦截器。
  这就是,整个同步请求的执行过程,是不是非常的简单呢?接下来,我们来看看异步请求的。

2. 异步请求

  异步请求的准备工作跟同步请求差不多,都是先创建OkHttpClient对象、然后创建Request对象,再创建Call对象,最后调用相应的方法来执行执行这个请求,异步请求调用的是enqueue方法。我们来看看enqueue方法:

 @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  } 

  前面部分,我们可以不看,重点在最后一行。我们先来看看Dispatcherenqueue方法:

 synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  } 

  首先,先判断当前异步请求的数目是否超过我们设置的值,这里默认值是64,其次在判断当前的请求的主机是否超过了5个,都不超过的话,那么就将当前的Call添加到异步队列中,然后将当前的Call对象提交到线程池中去执行;如果超过的话,那么就将当前的Call对象放入等待队列中去。
  我们知道,在同步请求中,是通过调用getResponseWithInterceptorChain方法来进行网络请求的,但是在这个异步请求过程中,我们并没有发现getResponseWithInterceptorChain方法的调用,难道是我们分析错了吗?并没有,我们需要在AsyncCall里面来寻找答案。
  我们先来看看AsyncCall的结构:

final class AsyncCall extends NamedRunnable {
} 

  AsyncCall是继承于NamedRunnable的,我们再去看看NamedRunnable:

public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
} 

  在NamedRunnable中,我们发现在run方法中调用了execute方法,最后我们回到了AsyncCall中来,不过这次,我们只需要关注execute方法就行了。

 @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  } 

  还是熟悉的味道,跟同步请求的差不多。但是这里我们需要注意的是,从这里可以看出,CallbackonFailure方法和onResponse方法都是在子线程中执行的,并没有通过Handler将消息传递到主线程。这里需要特别注意。

3.总结

  OkHttp两种请求方式的执行流程是比较简单,现在对它做一个简单的总结。
  1.同步请求和异步请求的在执行,都必须做同样的步骤来准备。包括OkHttpClient的创建,Request的创建,Call的创建。
  2.同步请求是调用execute方法来执行,在execute方法中,通过调用getResponseWithInterceptorChain方法来真正进行网络请求,同时每一个Call只能执行一次。
  3.异步请求是通过调用enqueue方法,最后提交到一个线程里面执行的。这里需要的注意的是CallbackonFailure方法和onResponse方法都是在子线程执行的。

收藏
评论区

相关推荐

Retrofit 动态修改BaseUrl 操作
开发中会遇到URL需要动态切换,若你还是通过gradle打包切换是否不太灵活,下面将介绍一下retrofit基于OKhttp中动态修改URL; 原理添加拦截器,在retrofit中的现实更加灵活了如下代码: language private RetrofitUrlManager() { if (DEPENDENCY_OKHTT
OkHttp 源码分析 - Okhttp同步请求、异步请求过程
RxJava源码的基础部分分析的差不多,后续如果有新的内容话,会继续的补充。从今天开始,我们来看看OkHttp的相关源码。OkHttp的源码过于复杂,涉及到的方面非常的多,本系列文章目的是打通Okhttp的整个执行流程,不对某一个细节重点分析。   本篇文章是本系列文章的第一篇,我们先从最简单的Okhttp使用入手,进而分析Okhttp两种请求方式的流程。
Retrofit封装Okhttp逻辑原理
总结自retrofit封装了Okhttp 本身并不能进行网络请求。只能在Android使用的网络请求框架。1.png2.pngrequest:统一完成(post/get/...) 回调陷阱:完成上一步网络请求才能进行下一步网络请求。3.pngRetrofit简化了网络请求。优化了网络请求的使用。4.png5.png7.pngbuild设计模式:参数》5个;
java常见的 http 请求库比较
java常见的http请求库有httpclient,RestTemplate,OKhttp,更高层次封装的 feign、retrofit ##1、HttpClient HttpClient:代码复杂,还得操心资源回收等。代码很复杂,冗余代码多,不建议直接使用。 ##2、RestTemplate RestTemplate: 是 Spring 提供的用于访问
Volley
OkHttp可以作为Volley底层传输协议,速度更快,传大量图片建议使用。OkHttp更多功能请看**[OkHttp的使用](https://my.oschina.net/zhangqie/blog/807474)** xUtils 支持大文件上传,更全面的http请求协议支持(10种谓词),拥有更加灵活的ORM,更多的事件注解支持且不受混淆影响...
Android+Spring Boot 选择+上传+下载文件
2021.02.03更新 ============ 1 概述 ==== 前端`Android`,上传与下载文件,使用`OkHttp`处理请求,后端使用`Spring Boot`,处理`Android`发送来的上传与下载请求。这个其实不难,就是特别多奇奇怪怪的坑,因此,就一句话, 希望各位读者能少走弯路。 2 环境 ==== * `Win10` *
Android中使用OkHttp的五种请求方式及注意事项
### 文章目录 * 一、环境说明 * * * 1、在gradle中引入依赖 * 2、AndroidManifest中开启网络权限 * 3、搭建测试接口 * 4、编写OkHttp工具类 * 二、发送请求 * * * 1.发送无参的Get请求 *
Android调试神器stetho使用详解和改造
> 本文由云+社区发表 > > 作者:NaOH 概述 -- * * * > stetho是Facebook开源的一个Android调试工具,项目地址:facebook/stetho 通过Stetho,开发者可以使用chrome的inspect功能,对Android应用进行调试和查看。 功能概述 stetho提供的功能主要有: * Networ
OKHttp源码学习
1.HttpURLConnection 1 public class HttpURLConnectionGetAndPost { 2 private String urlAddress = "xxxx"; 3 4 public void doGet(String method, String s)
OKHttp的配置Cookie持久化
做Cookie的持久化。 OKHttp3.0之后和之前做Cookie持久化有了点区别下面直接上代码: public static String httpPost(String url,String json) { String res = ""; OkHttpClient okHttpClient = new OkHttpClient()
OkHttp三问—百度真题
来吧,今天说说常用的网络框架OKHttp,也是现在Android所用的原生网络框架(`Android 4.4`开始,`HttpURLConnection`的底层实现被`Google`改成了`OkHttp`),GOGOGO! * OKHttp有哪些拦截器,分别起什么作用 * OkHttp怎么实现连接池 * OkHttp里面用到
OkHttp请求耗时统计
#### 目录介绍 * 01.先提问一个问题 * 02.EventListener回调原理 * 03.请求开始结束监听 * 04.dns解析开始结束监听 * 05.连接开始结束监听 * 06.TLS连接开始结束监听 * 07.连接绑定和释放监听 * 08.request请求监听 * 09.response响应监听 *
OkHttp配置HTTPS访问+服务器部署
1 概述 ==== OkHttp配置HTTPS访问,核心为以下三个部分: * sslSocketFactory() * HostnameVerifier * X509TrustManager 第一个是ssl套接字工厂,第二个用来验证主机名,第三个是证书信任器管理类.通过OkHttp实现HTTPS访问需要自己实现以上三部分.另外还简单提及了服
Retrofit源码解析(上)
简介 Retrofit是Square公司开发的一款针对Android网络请求的框架,官网地址http://square.github.io/retrofit/ ,在官网上有这样的一句话介绍retrofit,A type-safe HTTP client for Android and Java。我们知道Retrofit底层是基于OKHttp实现的。对ok
Retrofit网络框架入门使用
1.简单介绍 ====== retrofit事实上就是对okhttp做了进一步一层封装优化。 我们仅仅须要通过简单的配置就能使用retrofit来进行网络请求了。 Retrofit能够直接返回Bean对象,比如假设我们进行一个网络接口的请求。返回来一串json字符串。那么这个时候一般我们都要拿到这个json字符串后进行解析得到相应的Bean对象,Ret