源码系列-OkHttp

Posted 安卓圈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码系列-OkHttp相关的知识,希望对你有一定的参考价值。

前面讲到了get请求,下面我们来看看post请求

package okhttp3.guide;
import java.io.IOException;import okhttp3.MediaType;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.RequestBody;import okhttp3.Response;
public class PostExample { public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException { RequestBody body = RequestBody.create(json, JSON); Request request = new Request.Builder() .url(url) .post(body) .build(); try (Response response = client.newCall(request).execute()) { return response.body().string(); } }
String bowlingJson(String player1, String player2) { return "{'winCondition':'HIGH_SCORE'," + "'name':'Bowling'," + "'round':4," + "'lastSaved':1367702411696," + "'dateStarted':1367702378785," + "'players':[" + "{'name':'" + player1 + "','history':[10,8,6,7,8],'color':-13388315,'total':39}," + "{'name':'" + player2 + "','history':[6,10,5,10,10],'color':-48060,'total':41}" + "]}"; }
public static void main(String[] args) throws IOException { PostExample example = new PostExample(); String json = example.bowlingJson("Jesse", "Jake"); String response = example.post("http://www.roundsapp.com/post", json); System.out.println(response); }}

第一步。构造函数

OkHttpClient client = new OkHttpClient();

源码:

public OkHttpClient() { this(new Builder());}

Builder()方法里是一堆变量和对象的初始化

第二步。构造请求body

RequestBody body = RequestBody.create(json, JSON);

RequestBody类中有多个重载方法create,我们来看第二个参数为string的方法

public static RequestBody create(@Nullable MediaType contentType, String content) { Charset charset = UTF_8; if (contentType != null) { charset = contentType.charset(); if (charset == null) { charset = UTF_8; contentType = MediaType.parse(contentType + "; charset=utf-8"); } } byte[] bytes = content.getBytes(charset); return create(contentType, bytes);}

前面就是设置字符类型,然后把String转为字节数组了,最后调用了另外一个create方法

第二个create方法又调用了第三个create方法,好吧,源码的日常操作

public static RequestBody create(final @Nullable MediaType contentType, final byte[] content, final int offset, final int byteCount) { if (content == null) throw new NullPointerException("content == null"); Util.checkOffsetAndCount(content.length, offset, byteCount); return new RequestBody() { @Override public @Nullable MediaType contentType() { return contentType; }
@Override public long contentLength() { return byteCount; }
@Override public void writeTo(BufferedSink sink) throws IOException { sink.write(content, offset, byteCount); } };}

最后return一个RequestBody,还重写了三个内部方法。BufferedSink百度了下是Okio里的一个缓存字符串,就是把构造的json字符串存到一个缓存中去,后面再用

第三步。构造请求

Request request = new Request.Builder() .url(url) .post(body) .build();

1)Request的内部类Builder

public Builder() { this.method = "GET"; this.headers = new Headers.Builder();}

默认是GET请求方式,后面会修正;第二初始化了一个Headers的内部类Builder,没有做什么实质的操作

2)Request的内部类Builder的url方法

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); }
return url(HttpUrl.get(url));}

进到HttpUrl类中

public static HttpUrl get(String url) { return new Builder().parse(null, url).build();}

这里面Builder()里面有个ArrayList:encodedPathSegments;parse()方法里面是一系列参数解析;build()方法如下

public HttpUrl build() { if (scheme == null) throw new IllegalStateException("scheme == null"); if (host == null) throw new IllegalStateException("host == null"); return new HttpUrl(this);}

HttpUrl的构造函数里面是一些对象和变量的初始化

3)Request的内部类Builder的post方法

public Builder post(RequestBody body) { return method("POST", body);}

前面的请求方式默认是GET,这里修改为了POST

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

前面4个if全是异常判断,后面是method和body赋值

4)Request的内部类Builder的post方法

public Request build() { if (url == null) throw new IllegalStateException("url == null"); return new Request(this);}

Requst的构造函数

Request(Builder builder) { this.url = builder.url; this.method = builder.method; this.headers = builder.headers.build(); this.body = builder.body; this.tags = Util.immutableMap(builder.tags);}

第四步:

Response response = client.newCall(request).execute()

OkHttpClient的newCall方法

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

进到RealCall类中

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.transmitter = new Transmitter(client, call); return call;}

Transmitter构造函数中有个这个Internal.instance.realConnectionPool(client.connectionPool())可以找到OkHttpClient的static代码块执行后返回了一个RealConnectionPool

private static final Executor executor = new ThreadPoolExecutor(0 /* corePoolSize */, Integer.MAX_VALUE /* maximumPoolSize */, 60L /* keepAliveTime */, TimeUnit.SECONDS, new SynchronousQueue<>(), Util.threadFactory("OkHttp ConnectionPool", true));

RealConnectionPool一看就知道是0个核心线程,最大值个非核心线程的线程池;这里还出现了一个Deque双端队列,即队列的升级版,两个端口都可以进出元素,更加灵活

最后就是执行请求execute方法,实际执行的是RealCall的execute方法

@Override public Response execute() throws IOException { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } transmitter.timeoutEnter(); transmitter.callStart(); try { client.dispatcher().executed(this); return getResponseWithInterceptorChain(); } finally { client.dispatcher().finished(this); }}

下面就是其他博客常说的链式拦截器

Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. 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()); } interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0, originalRequest, this, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false; try { Response response = chain.proceed(originalRequest); if (transmitter.isCanceled()) { closeQuietly(response); throw new IOException("Canceled"); } return response; } catch (IOException e) { calledNoMoreExchanges = true; throw transmitter.noMoreExchanges(e); } finally { if (!calledNoMoreExchanges) { transmitter.noMoreExchanges(null); } }}

每个拦截器里都调用chain.proceed,这样所有的拦截器就形成链条

五。另外,还有个异步请求

private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception { Request request = new Request.Builder() .url("http://publicobject.com/helloworld.txt") .build();
client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); }
@Override public void onResponse(Call call, Response response) throws IOException { try (ResponseBody responseBody = response.body()) { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers(); for (int i = 0, size = responseHeaders.size(); i < size; i++) { System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i)); }
System.out.println(responseBody.string()); } } });}

RealCall的enqueue方法

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

client.dispatcher().enqueue(new AsyncCall(responseCallback))

进到Dispatcher类的enqueue方法

void enqueue(AsyncCall call) { synchronized (this) { readyAsyncCalls.add(call);
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to // the same host. if (!call.get().forWebSocket) { AsyncCall existingCall = findExistingCallWithHost(call.host()); if (existingCall != null) call.reuseCallsPerHostFrom(existingCall); } } promoteAndExecute();}

readyAsyncCalls是一个Deque双端队列

最后的promoteAndExecute方法里有行代码

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

executorService也是一个核心线程为0个,非核心线程为最大个数的线程池

最后进到RealCall的内部类AsyncCall的executeOn方法

void executeOn(ExecutorService executorService) { assert (!Thread.holdsLock(client.dispatcher())); boolean success = false; try { executorService.execute(this); success = true; } catch (RejectedExecutionException e) { InterruptedIOException ioException = new InterruptedIOException("executor rejected"); ioException.initCause(e); transmitter.noMoreExchanges(ioException); responseCallback.onFailure(RealCall.this, ioException); } finally { if (!success) { client.dispatcher().finished(this); // This call is no longer running! } }}

主要就是executorService.execute方法

可以看出GET请求和POST请求还是差不多的,关键的就是各个拦截器里面的实现了

以上是关于源码系列-OkHttp的主要内容,如果未能解决你的问题,请参考以下文章

源码系列-OkHttp

OkHttpOkHttp 源码分析 ( OkHttpClient.Builder 构造器源码分析 )

OkHttpOkHttp 源码分析 ( 网络框架封装 | OkHttp 4 迁移 | OkHttp 建造者模式 )

OkHttpOkHttp 源码分析 ( 同步 / 异步 Request 请求执行原理分析 )

Android 10大开源常用框架源码解析 系列 网络框架之一 OkHttp杂题

《OkHttp源码分析》之 OkHttp的缓存管理