源码系列-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() {
public MediaType contentType() {
return contentType;
}
public long contentLength() {
return byteCount;
}
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, 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方法
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方法
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() {
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
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方法
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的主要内容,如果未能解决你的问题,请参考以下文章
OkHttpOkHttp 源码分析 ( OkHttpClient.Builder 构造器源码分析 )
OkHttpOkHttp 源码分析 ( 网络框架封装 | OkHttp 4 迁移 | OkHttp 建造者模式 )
OkHttpOkHttp 源码分析 ( 同步 / 异步 Request 请求执行原理分析 )