OkHttp源码分析一从用法来看

OkHttp优点
  • 支持HTTP2/SPDY黑科技
  • socket自动选择最好的路线,并支持自动重连
  • 拥有自动维护的socket连接池,减少握手次数
  • 拥有队列线程池,轻松写并发
  • 拥有Interceptors轻松处理请求和响应
  • 基于Headers的缓存策略
OkHttp的使用

一般情况下,我们会这样使用OkHttp:

 OkHttpClient client = new OkHttpClient.Builder().connectTimeout(1000,
                TimeUnit.SECONDS)
                .cookieJar(new CookieJar() {
    
                    @Override
                    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
    

                    }

                    @Override
                    public List<Cookie> loadForRequest(HttpUrl url) {
    
                        return null;
                    }
                }).build();
        Request request = new Request.Builder().url("http://www.baidu.com").build();
        Call call = client.newCall(request);
        //异步
        call.enqueue(new Callback() {
    
            @Override
            public void onFailure(Call call, IOException e) {
    

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
    

            }
        });

        //同步
        try {
    
            Response response = call.execute();
        }catch (IOException e){
    
            e.printStackTrace();
        }

OkHttpClient

先看它官方对它的描述:

Factory for {@linkplain Call calls}, which can be used to send HTTP requests and read their responses.
它是一个执行调用请求Call的工厂,可以用来发送HTTP请求和读取HTTP的response

 OkHttpClients should be shared
 OKHttpClients应该被共享

OkHttp performs best when you create a single {@code OkHttpClient} instance 
and reuse it for all of your HTTP calls. This is because each client holds its 
own connection pool and thread pools. Reusing connections and threads reduces 
latency and saves memory. Conversely, creating a client for each request 
wastes resources on idle pools.

OKHttp最好只创建一个实例,这样就可以对所有HTTP Call进行复用。
这是因为每一个Client都有自己的连接池和线程池。复用连接池和线程池可以减少延迟和节约内存。
相反地,如果对每个请求都创建一个client,对于空闲的pools会造成浪费资源。

我们一般是这样来创建OKHttpClient的:

new OkHttpClient.Builder().build();

那么我们就先看看这个Builder:

/**
* Builder是一个OKHttpClient的静态内部类
*/
public static final class Builder {
    
	//调度器,里面包含了线程池和三个队列(readyAsynCalls:保存等待执行的异步请求)
    Dispatcher dispatcher;
    //代理类,默认有三种代理模式:
    //DIRECT(直连),HTTP(http代理),SOCKS(socks代理)
    @Nullable Proxy proxy;
    //协议集合,协议类,用来表示使用的协议版本,比如HTTP 1.0等
    List<Protocol> protocols;
    //连接规范,用于配置Socket连接层,对于HTTPS,还能配置安全传输层协议版本和密码套件
    List<ConnectionSpec> connectionSpecs;
    
    //拦截器
    final List<Interceptor> interceptors = new ArrayList<>();
    final List<Interceptor> networkInterceptors = new ArrayList<>();

    EventListener.Factory eventListenerFactory;
    //代理选择类,默认不适用代理,即使用直连方式
    //我们可以自定义配置,以指定URI使用某种代理,类似代理软件的PAC功能
    ProxySelector proxySelector;
    //Cookie的保存获取
    CookieJar cookieJar;
    //缓存类,内部使用了DiskLruCache来进行管理缓存,
    //匹配缓存的机制不仅仅是根据url,还会根据请求方法和请起头来验证是否可以响应缓存
    //仅支持GET请求的缓存
    @Nullable Cache cache;
    //内置缓存
    @Nullable InternalCache internalCache;
    //Socket的抽象工厂,通过createSocket来创建Socket
    SocketFactory socketFactory;
    //安全套接层工厂,HTTPS相关,用于创建SSLSocket。
    //一般配置HTTPS证书信任问题都需要从这里入手
    @Nullable SSLSocketFactory sslSocketFactory;
    //证书链清洁器,HTTPS相关
    //用于从[Java]的TLS API构建的原始数组中统计有效的证书链,
    //然后清除跟TLS握手不相关的证书,提取可信任的证书以便可以受益于证书锁机制。
    @Nullable CertificateChainCleaner certificateChainCleaner;
    //主机名验证器,与HTTPS中的SSL相关,
    //当握手时如果URL的主机名不是可识别的主机,就会要求进行主机名验证
    HostnameVerifier hostnameVerifier;
    //证书所,HTTPS相关,用于约束哪些证书可以被信任,
    //可以防止一些已知或未知的中间证书机构带来的攻击行为。
    CertificatePinner certificatePinner;
    //身份认证器,当连接提示为授权时,可以通过重新设置请求头来响应一个新的Request
    //状态码401表示远程服务器请求授权,407表示代理服务器请求授权。
    //该认证器在需要时会被RetryAndFollowUpInterceptor触发
    Authenticator proxyAuthenticator;
    Authenticator authenticator;

	//连接池
    ConnectionPool connectionPool;
    Dns dns;
    boolean followSslRedirects;//是否遵循SSL重定向
    boolean followRedirects;//是否重定向
    boolean retryOnConnectionFailure;//失败是否重新连接
    int connectTimeout;//超时时间
    int readTimeout;//读取超时
    int writeTimeout;//写入超时
    int pingInterval;

    public Builder() {
    
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;//默认支持的协议
      connectionSpecs = DEFAULT_CONNECTION_SPECS;//默认的连接规范
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();//默认代理选择器,直连
      cookieJar = CookieJar.NO_COOKIES;//默认不进行管理Cookie
      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;
    }
    ... ... 
 }

我们通过源码可以知道Builder只是对OKHttp进行一些配置,在里面有一个Dispatcher,调度器:
先看官方对它的描述:

/**
 * Policy on when async requests are executed.
 * 何时进行异步请求的策略
 *
 * <p>Each dispatcher uses an {@link ExecutorService} to run calls internally. 
 * If you supply your own executor, it should be able to run
 *{@linkplain #getMaxRequests the configured maximum} number
 * of calls concurrently.
 */

Dispatcher是一个异步执行的策略,在Dispatcher中每一个请求都是使用ExecutorService来执行的

public final class Dispatcher {
    
  private int maxRequests = 64;//最大并发数位64,同时请求
  private int maxRequestsPerHost = 5;//每个主机的最大请求数为5
  private @Nullable Runnable idleCallback;//闲置接口

  /** Executes calls. Created lazily. */
  private @Nullable ExecutorService executorService;//线程池

  /** 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<>();

  public Dispatcher(ExecutorService executorService) {
    
    this.executorService = executorService;
  }

  public Dispatcher() {
    
  }
... ... 
}

在我们创建OKHttpClient的时候,我们是使用Builder.build()方法:

 public OkHttpClient build() {
    
      return new OkHttpClient(this);
    }

然后我们使用Call call = client.newCall(request);;来创建一个Call:

 /**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

 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;
  }

我们可以看到实际上是创建了一个RealCalld的实例,ReadlCall是Call的实现类:

final class RealCall implements Call {
    
  final OkHttpClient client;
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;

  /**
   * There is a cycle between the {@link Call} and {@link EventListener} that makes this awkward.
   * This will be set after we create the call instance then create the event listener instance.
   */
  private EventListener eventListener;

  /** The application's original request unadulterated by redirects or auth headers. */
  final Request originalRequest;
  final boolean forWebSocket;

  // Guarded by this.
  private boolean executed;

  private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }
 ... ...
}

然后我们会执行call.enqueue(new Callback())的方法:实际上是RealCall

  @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));
  }

我们可以看到最后是交给了dispatcher:

  synchronized void enqueue(AsyncCall call) {
    
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    
    //如果真正运行的请求数量小于最大请求数且主机请求数小于最大每个主机请求数
    //加入正在进行异步请求的队列
      runningAsyncCalls.add(call);
      //线程池执行这个请求
      executorService().execute(call);
    } else {
    
    //加入准备队列
      readyAsyncCalls.add(call);
    }
  }

 public synchronized ExecutorService executorService() {
    
    if (executorService == null) {
    
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

 public static ThreadFactory threadFactory(final String name, final boolean daemon) {
    
    return new ThreadFactory() {
    
      @Override public Thread newThread(Runnable runnable) {
    
        Thread result = new Thread(runnable, name);
        result.setDaemon(daemon);
        return result;
      }
    };
  }

在ThreadPoolExecutor中有三种队列:

  • SynchronousQueue:是无界的,一种无缓冲的等待队列,在某次添加元素后必须等待其他线程取走后才能继续添加,可以认为SynchronousQueue是一个缓存值为1的阻塞队列,但是 isEmpty()方法永远返回是true
  • LinkedBlockingQueue:无界的,一个无界缓存等待队列。基于链表的阻塞队列,内部维持着一个数据缓冲队列, 当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。
  • ArrayListBlockingQueue:一个有界缓存的等待队列。基于数组的阻塞队列,同LinkedBlockingQueue类似,内部维持着一个定长数据缓冲队列(该队列由数组构成)。

更多的再百度百度。

回到源码上,在我们执行client.dispatcher().enqueue(new AsyncCall(responseCallback));后,会把请求交给线程池,给线程池传进的是AsyncCall这个的实例,我们知道,在线程池会,最终会调用Runnable的run方法:
那么这个Runnable其实是AsyncCall,我们来看看AsyncCall:

final class AsyncCall extends NamedRunnable {
    
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
    
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }
    ... ...
  }

/**
 * Runnable implementation which always sets its thread name.
 */
public abstract class NamedRunnable implements Runnable {
    
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    
    this.name = Util.format
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值