「简学」OKhttp


Okhttp简介

HTTP是现代应用常用的一种交换数据和媒体的网络方式,高效地使用HTTP能让资源加载更快,节省带宽。OkHttp是一个高效的HTTP客户端,它有以下默认特性:

  • 支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接
  • 连接池减少请求延时
  • 透明的GZIP压缩减少响应数据的大小
  • 缓存响应内容,避免一些完全重复的请求

当网络出现问题的时候OkHttp依然坚守自己的职责,它会自动恢复一般的连接问题,如果你的服务有多个IP地址,当第一个IP请求失败时,OkHttp会交替尝试你配置的其他IP,OkHttp使用现代TLS技术(SNI, ALPN)初始化新的连接,当握手失败时会回退到TLS 1.0。

OkHttp官网地址:http://square.github.io/okhttp/

pom依赖:



    com.squareup.okhttp3
    okhttp
    4.7.2

Okhttp使用

本文使用的OkHttp版本为4.7.2。OkHttp的核心类主要有OkHttpClient,Dispatcher,Call,Request,Response,Interceptor,Chain。其中OkHttpClient是负责管理多个Call的组织者,而每个Call又包含一个Request和Response,并且Call中的回调用于提供响应结果。要完成一次网络请求,我们需要告诉Call需要处理的Request是什么样的,例如它的URL是什么,然后将Call交给OkHttpClient。OkHttpClient仅对本次请求做一些配置,例如指定缓存路径,它会让Dispatcher去决定何时执行Call。而Dispatcher的底层实现就是一个由OkHttp默认实现的线程池,它将最终执行Call中的.run()方法。最后的Interceptor和Chain将用于数据的拦截处理。OkHttp提供两种方式提交网络请求,分别是Call.execute()和Call.enqueue(Callback),前者会阻塞线程,后者加入队列异步执行。通过调用response.body().string()我们可以得到响应的body部分并以String形式返回,但值得注意的是.string()只能调用一次。

创建并配置OkHttpClient 

OkHttpClient通过内部的Builder类来创建。

OkHttpClient.Builder默认参数:

public Builder() {
    // dispatch。负责分配处理异步任务
    this.dispatcher = new Dispatcher();
    // 支持的协议,默认支持http2和http1.1
    this.protocols = OkHttpClient.DEFAULT_PROTOCOLS;
    // 指定socket链接配置。默认:MODERN_TLS和CLEARTEXT
    this.connectionSpecs = OkHttpClient.DEFAULT_CONNECTION_SPECS;
    // 监听http请求过程中涉及的各种事件。包括:开始请求、dns解析开始/结束、链接开始等等。默认是的Listener
    this.eventListenerFactory = EventListener.factory(EventListener.NONE);
    // 设置代理
    this.proxySelector = ProxySelector.getDefault();
    // 设置cookie
    this.cookieJar = CookieJar.NO_COOKIES;
    // 设置创建Socket链接的Factory
    this.socketFactory = SocketFactory.getDefault();
    // 验证域名和证书的域名是否匹配
    this.hostnameVerifier = OkHostnameVerifier.INSTANCE;
    // 设置信任的证书,不信任之外的证书,默认是空的
    this.certificatePinner = CertificatePinner.DEFAULT;
    // 与身份认证有关。详见:https://square.github.io/okhttp/3.x/okhttp/okhttp3/Authenticator.html
    this.proxyAuthenticator = Authenticator.NONE;
    // 与身份认证有关。详见:https://square.github.io/okhttp/3.x/okhttp/okhttp3/Authenticator.html
    this.authenticator = Authenticator.NONE;
    // 连接池-缓存链接和清理空闲的连接
    this.connectionPool = new ConnectionPool();
    // 域名解析
    this.dns = Dns.SYSTEM;
    // 是否支持ssl重定向
    this.followSslRedirects = true;
    // 是否支持重定向
    this.followRedirects = true;
    // 失败后是否重试
    this.retryOnConnectionFailure = true;
    // 链接超时时间
    this.connectTimeout = 10000;
    // 读超时时间
    this.readTimeout = 10000;
    // 写超时时间
    this.writeTimeout = 10000;
    // ping的时间间隔。如果使用WebSocket请求设置pingInterval,以保活
    this.pingInterval = 0;
}

Dispatcher-任务调度器:

public final class Dispatcher {
    // 最大的异步请求数
    private int maxRequests = 64;
    // 单个host最大的请求数
    private int maxRequestsPerHost = 5;
    @Nullable
    private Runnable idleCallback;
    @Nullable
    // 执行异步请求的线程池
    private ExecutorService executorService;
    // 尚未执行的请求
    private final Deque readyAsyncCalls = new ArrayDeque();
    // 正在执行的异步请求
    private final Deque runningAsyncCalls = new ArrayDeque();
    // 正在执行的同步请求
    private final Deque runningSyncCalls = new ArrayDeque();
    // 设置异步线程池
    public Dispatcher(ExecutorService executorService) {
        this.executorService = executorService;
    }
    public Dispatcher() {
    }
    public synchronized ExecutorService executorService() {
        if (this.executorService == null) {
            // 默认的异步线程池
            this.executorService = new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
        }
        return this.executorService;
    }
    // 异步任务,判断异步任务数和单个host的请求数
    synchronized void enqueue(AsyncCall call) {
        if (this.runningAsyncCalls.size() < this.maxRequests && this.runningCallsForHost(call) < this.maxRequestsPerHost) {
            this.runningAsyncCalls.add(call);
            this.executorService().execute(call);
        } else {
            this.readyAsyncCalls.add(call);
        }
    }
}

ConnectionTool-缓存链接和清理空闲的连接:

public final class ConnectionPool {
    // 负责清理空闲连接的线程池
    private static final Executor executor;
    // 最大空闲的连接数-默认值:5
    private final int maxIdleConnections;
    // 最大的空闲时间-默认值:5分钟
    private final long keepAliveDurationNs;
    // 负责清理空闲连接的Runnable
    private final Runnable cleanupRunnable;
    // 所有的连接
    private final Deque connections;
    final RouteDatabase routeDatabase;
    boolean cleanupRunning;
    
    public ConnectionPool() {
        this(5, 5L, TimeUnit.MINUTES);
    }
    
    public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
        ////
    }
    
    static {
        // 负责清理空闲连接的线程池
        executor = new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp ConnectionPool", true));
    }
}

发送请求

常见的Okhttp配置

OkHttpClient client = new OkHttpClient.Builder()
        .connectTimeout(60, TimeUnit.SECONDS)      //设置连接超时
        .readTimeout(60, TimeUnit.SECONDS)         //设置读超时
        .writeTimeout(60, TimeUnit.SECONDS)        //设置写超时
        .retryOnConnectionFailure(true)            //是否自动重连
        .build();

OkHttpClient 封装了HttpClient,性能类似,HttpClient可以配置更多内容,OkHttpClient使用更简单。

相关