源码系列--OkHttp(2)
OkHttp官网地址:https://square.github.io/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); } }
Github下载源码地址https://github.com/square/okhttp
第一步。构造函数
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双端队列
@Nullable private AsyncCall findExistingCallWithHost(String host) { for (AsyncCall existingCall : runningAsyncCalls) { if (existingCall.host().equals(host)) return existingCall; } for (AsyncCall existingCall : readyAsyncCalls) { if (existingCall.host().equals(host)) return existingCall; } return null; }
最后的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请求还是差不多的,关键的就是各个拦截器里面的实现了
欢迎关注我的微信公众号:安卓圈