Okhttp3源码解析
来源:互联网 发布:民治编程语言培训 编辑:程序博客网 时间:2024/06/06 08:31
上面的图是自己阅读源码整理出来的流程图,具体的会在下面一一讲解和展示出来。
Okhttp是square公司出品的,同时出品的还有Okio,Okio顾名思义就是对IO的处理,毫无疑问,okhttp的IO处理当然也是基于Okio的,后面我也计划写一篇关于Okio的源码解析。扯了这么多,就正式开始我们今天的Okhttp源码解析旅程吧!
OKHttpClient
创建 OkHttpClient 对象
OkHttpClient client = new OkHttpClient();
static { Internal.instance = new Internal() { @Override public void addLenient(Headers.Builder builder, String line) { builder.addLenient(line); } @Override public void addLenient(Headers.Builder builder, String name, String value) { builder.addLenient(name, value); } @Override public void setCache(OkHttpClient.Builder builder, InternalCache internalCache) { builder.setInternalCache(internalCache); } @Override public boolean connectionBecameIdle( ConnectionPool pool, RealConnection connection) { return pool.connectionBecameIdle(connection); } @Override public RealConnection get(ConnectionPool pool, Address address, StreamAllocation streamAllocation, Route route) { return pool.get(address, streamAllocation, route); } @Override public boolean equalsNonHost(Address a, Address b) { return a.equalsNonHost(b); } @Override public Socket deduplicate( ConnectionPool pool, Address address, StreamAllocation streamAllocation) { return pool.deduplicate(address, streamAllocation); } @Override public void put(ConnectionPool pool, RealConnection connection) { pool.put(connection); } @Override public RouteDatabase routeDatabase(ConnectionPool connectionPool) { return connectionPool.routeDatabase; } @Override public int code(Response.Builder responseBuilder) { return responseBuilder.code; } @Override public void apply(ConnectionSpec tlsConfiguration, SSLSocket sslSocket, boolean isFallback) { tlsConfiguration.apply(sslSocket, isFallback); } @Override public HttpUrl getHttpUrlChecked(String url) throws MalformedURLException, UnknownHostException { return HttpUrl.getChecked(url); } @Override public StreamAllocation streamAllocation(Call call) { return ((RealCall) call).streamAllocation(); } @Override public Call newWebSocketCall(OkHttpClient client, Request originalRequest) { return new RealCall(client, originalRequest, true); } }; }
打开OkhttpClient源码,最让人注目的就是这一大坨,这些是做什么的呢?先留个悬念,在后面的解析中我们会使用到他,到时候我们再做详解,继续!!!
public OkHttpClient() { this(new Builder()); }
没错,下一个就是他的构造方法,咦??初始化了一个Builder,Bulider又做了些什么东西呢?
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; }
在Builder的构造方法里我们需要关注两个类的实例化,一个是dispatcher = new Dispatcher();一个是connectionPool = new ConnectionPool();这两个的实例化会在后面使用,留个印象即可,至此,okhttpclient对象已经创建完毕,接下来就是我们的Request的创建了。
Request创建
Request request = new Request.Builder() .url(url) .build();
/** * An HTTP request. Instances of this class are immutable if their {@link #body} is null or itself * immutable. */public final class Request { final HttpUrl url; final String method; final Headers headers; final @Nullable RequestBody body; final Object tag; private volatile CacheControl cacheControl; // Lazily initialized. Request(Builder builder) { this.url = builder.url; this.method = builder.method; this.headers = builder.headers.build(); this.body = builder.body; this.tag = builder.tag != null ? builder.tag : this; } public HttpUrl url() { return url; } public String method() { return method; } public Headers headers() { return headers; } public String header(String name) { return headers.get(name); } public List<String> headers(String name) { return headers.values(name); } public @Nullable RequestBody body() { return body; } public Object tag() { return tag; } public Builder newBuilder() { return new Builder(this); } /** * Returns the cache control directives for this response. This is never null, even if this * response contains no {@code Cache-Control} header. */ public CacheControl cacheControl() { CacheControl result = cacheControl; return result != null ? result : (cacheControl = CacheControl.parse(headers)); } public boolean isHttps() { return url.isHttps(); } @Override public String toString() { return "Request{method=" + method + ", url=" + url + ", tag=" + (tag != this ? tag : null) + '}'; } public static class Builder { HttpUrl url; String method; Headers.Builder headers; RequestBody body; Object tag; public Builder() { this.method = "GET"; this.headers = new Headers.Builder(); } Builder(Request request) { this.url = request.url; this.method = request.method; this.body = request.body; this.tag = request.tag; this.headers = request.headers.newBuilder(); } public Builder url(HttpUrl url) { if (url == null) throw new NullPointerException("url == null"); this.url = url; return this; } /** * Sets the URL target of this request. * * @throws IllegalArgumentException if {@code url} is not a valid HTTP or HTTPS URL. Avoid this * exception by calling {@link HttpUrl#parse}; it returns null for invalid URLs. */ public Builder url(String url) { if (url == null) throw new NullPointerException("url == null"); // Silently replace web socket URLs with HTTP URLs. 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); } HttpUrl parsed = HttpUrl.parse(url); if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url); return url(parsed); } /** * Sets the URL target of this request. * * @throws IllegalArgumentException if the scheme of {@code url} is not {@code http} or {@code * https}. */ public Builder url(URL url) { if (url == null) throw new NullPointerException("url == null"); HttpUrl parsed = HttpUrl.get(url); if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url); return url(parsed); } /** * Sets the header named {@code name} to {@code value}. If this request already has any headers * with that name, they are all replaced. */ public Builder header(String name, String value) { headers.set(name, value); return this; } /** * Adds a header with {@code name} and {@code value}. Prefer this method for multiply-valued * headers like "Cookie". * * <p>Note that for some headers including {@code Content-Length} and {@code Content-Encoding}, * OkHttp may replace {@code value} with a header derived from the request body. */ public Builder addHeader(String name, String value) { headers.add(name, value); return this; } public Builder removeHeader(String name) { headers.removeAll(name); return this; } /** Removes all headers on this builder and adds {@code headers}. */ public Builder headers(Headers headers) { this.headers = headers.newBuilder(); return this; } /**设置缓存策略 * Sets this request's {@code Cache-Control} header, replacing any cache control headers already * present. If {@code cacheControl} doesn't define any directives, this clears this request's * cache-control headers. */ public Builder cacheControl(CacheControl cacheControl) { String value = cacheControl.toString(); if (value.isEmpty()) return removeHeader("Cache-Control"); return header("Cache-Control", value); } public Builder get() { return method("GET", null); } public Builder head() { return method("HEAD", null); } public Builder post(RequestBody body) { return method("POST", body); } public Builder delete(@Nullable RequestBody body) { return method("DELETE", body); } public Builder delete() { return delete(Util.EMPTY_REQUEST); } public Builder put(RequestBody body) { return method("PUT", body); } public Builder patch(RequestBody body) { return method("PATCH", body); } public Builder method(String method, @Nullable RequestBody body) { if (method == null) throw new NullPointerException("method == null"); if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0"); if (body != null && !HttpMethod.permitsRequestBody(method)) { throw new IllegalArgumentException("method " + method + " must not have a request body."); } if (body == null && HttpMethod.requiresRequestBody(method)) { throw new IllegalArgumentException("method " + method + " must have a request body."); } this.method = method; this.body = body; return this; } /** * Attaches {@code tag} to the request. It can be used later to cancel the request. If the tag * is unspecified or null, the request is canceled by using the request itself as the tag. */ public Builder tag(Object tag) { this.tag = tag; return this; } public Request build() { if (url == null) throw new IllegalStateException("url == null"); return new Request(this); } }}
可以看出,Request采用的构建者模式,没有什么可讲的,要说知识点的话,就是缓存。okhttp提供了两种缓存策略,一个是拦截器缓存(client层处理),一个是CacheControl缓存(request层处理),
缓存拦截器
class MyCacheInterceptor implements Interceptor{ @Override public Response intercept(Chain chain) throws IOException { Response originalResponse=chain.proceed(chain.request()); return originalResponse.newBuilder().removeHeader("prama").header("Cache-Control","max-age=60").build(); } }
定义好拦截器中后,我们可以添加到OKHttpClient中了。
OkHttpClient okHttpClient=new OkHttpClient.Builder() .addNetworkInterceptor(new MyCacheInterceptor()) .build();
CacheControl
okhttp中建议用 CacheControl 这个类来进行缓存策略的制定。它内部有两个很重要的静态实例。
/**强制使用网络请求 * Cache control request directives that require network validation of responses. Note that such * requests may be assisted by the cache via conditional GET requests. */ public static final CacheControl FORCE_NETWORK = new Builder().noCache().build(); /**强制使用本地缓存 * Cache control request directives that uses the cache only, even if the cached response is * stale. If the response isn't available in the cache or requires server validation, the call * will fail with a {@code 504 Unsatisfiable Request}. */ public static final CacheControl FORCE_CACHE = new Builder() .onlyIfCached() .maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS) .build();
使用方法如下:
Request request = new Request.Builder() .url("") //强制使用缓存 //.cacheControl(CacheControl.FORCE_CACHE) //强制使用网络 .cacheControl(CacheControl.FORCE_NETWORK) .build();
发起 HTTP 请求
//同步请求Response response = client.newCall(request).execute();//异步请求 okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { } });
无论是同步请求还是异步请求,都是由newCall调起的,那我们就看一下newCall的内部实现:
@Override public Call newCall(Request request) { return new RealCall(this, request, false /* for web socket */); }
如此看来功劳全在 RealCall 类了,下面我们一边分析同步网络请求的过程,一边了解 RealCall 的具体内容。
final class RealCall implements Call { final OkHttpClient client; final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor; final 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; RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) { final EventListener.Factory eventListenerFactory = client.eventListenerFactory(); this.client = client; this.originalRequest = originalRequest; this.forWebSocket = forWebSocket; this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket); // TODO(jwilson): this is unsafe publication and not threadsafe. this.eventListener = eventListenerFactory.create(this); } //同步请求 @Override public Response execute() throws IOException { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); try { client.dispatcher().executed(this); (1) Response result = getResponseWithInterceptorChain(); (2) if (result == null) throw new IOException("Canceled"); return result; } finally { client.dispatcher().finished(this); } } //异步请求 @Override public void enqueue(Callback responseCallback) { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); client.dispatcher().enqueue(new AsyncCall(responseCallback));(1) } private void captureCallStackTrace() { Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()"); retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace); }}
这里只截取了关键的代码块。
同步请求:
(1)利用 client.dispatcher().executed(this) 来进行实际执行
(2)调用 getResponseWithInterceptorChain() 函数获取 HTTP 返回结果
(3)最后还要通知 dispatcher 自己已经执行完毕
异步请求:
(1)调用dispathcher.enqueue(),参数为AsyncCall;
final class AsyncCall extends NamedRunnable { private final Callback responseCallback; AsyncCall(Callback responseCallback) { super("OkHttp %s", redactedUrl()); this.responseCallback = responseCallback; } String host() { return originalRequest.url().host(); } Request request() { return originalRequest; } RealCall get() { return RealCall.this; } @Override protected void execute() { boolean signalledCallback = false; try { Response response = getResponseWithInterceptorChain(); (1) 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 { responseCallback.onFailure(RealCall.this, e); } } finally { client.dispatcher().finished(this); } } }
/** * 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(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();}
由源码可以看出,其实AsyncCall就是一个线程,线程的execute方法里面同样调用getResponseWithInterceptorChain()获取Response。由此可以看出,无论是同步请求,还是异步请求都是通过getResponseWithInterceptorChain()方法获取Response的,毫无疑问,必须要看看内部如何实现的:
Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of 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)); Interceptor.Chain chain = new RealInterceptorChain( interceptors, null, null, null, 0, originalRequest); return chain.proceed(originalRequest); }
卧槽,竟然是一系列的拦截器组成的,所以下面的重点就是搞清楚每个拦截器的功能。由于篇幅的原因,下面专门用一篇博客讲解拦截器。敬请期待!!!!!
- Okhttp3源码解析
- OkHttp3源码解析
- OkHttp3源码解析
- Okhttp3使用 + 源码完全解析
- OkHttp3源码解析01-请求
- OkHttp3源码解析03-缓存
- OkHttp3.9源码解析(一)
- 望穿秋水:基于实例纵深解析Okhttp3源码
- Okhttp3源码(2)---CacheInterceptor解析
- OkHttp3.0源码解析---拦截器
- OkHttp3源码解析02-拦截器
- OkHttp3源码解析04-失败重连
- OkHttp3源码解析05-连接池
- OkHttp3深度源码解析(一),体会OkHttp3设计模式的妙处
- Okhttp3源码(1)---同步异步请求流程解析
- OkHttp3源码分析
- OkHttp3源码分析[综述]
- OkHttp3 源码解读
- 今日头条Go建千亿级微服务的实践
- [return sum(nums)-min(nums)*len(nums)leetcode453】 Minimum Moves to Equal Array Elements
- Android反编译apk
- spring+mybatis框架配置文件中使用${}占位符报错
- 改变echo 输出的颜色
- Okhttp3源码解析
- 菜鸟学习c、c++
- 如何使用adb命令发送keyevent
- 桶排序
- 本地导入jar包 报intellij 报inspects a maven model for resolution problems错误
- 关于Kaa物联网的概述
- OBS完美直播弹幕效果主播教程
- 如果给你选择_你更愿意在哪座城市的一隅敲代码?
- 1.1 编程语言与开发环境