前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >源码系列-OkHttp(2)

源码系列-OkHttp(2)

作者头像
用户3112896
发布2020-05-12 15:20:57
6040
发布2020-05-12 15:20:57
举报
文章被收录于专栏:安卓圈安卓圈

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

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

代码语言:javascript
复制
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

第一步。构造函数

代码语言:javascript
复制
OkHttpClient client = new OkHttpClient();

源码:

代码语言:javascript
复制
public OkHttpClient() {
  this(new Builder());
}

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

第二步。构造请求body

代码语言:javascript
复制
RequestBody body = RequestBody.create(json, JSON);

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

代码语言:javascript
复制
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方法,好吧,源码的日常操作

代码语言:javascript
复制
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字符串存到一个缓存中去,后面再用

第三步。构造请求

代码语言:javascript
复制
Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();

1)Request的内部类Builder

代码语言:javascript
复制
public Builder() {
  this.method = "GET";
  this.headers = new Headers.Builder();
}

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

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

代码语言:javascript
复制
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类中

代码语言:javascript
复制
public static HttpUrl get(String url) {
  return new Builder().parse(null, url).build();
}

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

代码语言:javascript
复制
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方法

代码语言:javascript
复制
public Builder post(RequestBody body) {
  return method("POST", body);
}

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

代码语言:javascript
复制
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方法

代码语言:javascript
复制
public Request build() {
  if (url == null) throw new IllegalStateException("url == null");
  return new Request(this);
}

Requst的构造函数

代码语言:javascript
复制
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);
}

第四步:

代码语言:javascript
复制
Response response = client.newCall(request).execute()

OkHttpClient的newCall方法

代码语言:javascript
复制
@Override public Call newCall(Request request) {
  return RealCall.newRealCall(this, request, false /* for web socket */);
}

进到RealCall类中

代码语言:javascript
复制
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

代码语言:javascript
复制
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方法

代码语言:javascript
复制
@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);
  }
}

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

代码语言:javascript
复制
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,这样所有的拦截器就形成链条

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

代码语言:javascript
复制
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方法

代码语言:javascript
复制
@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方法

代码语言:javascript
复制
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双端队列

代码语言:javascript
复制

最后的promoteAndExecute方法里有行代码

代码语言:javascript
复制
asyncCall.executeOn(executorService())
代码语言:javascript
复制
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方法

代码语言:javascript
复制
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请求还是差不多的,关键的就是各个拦截器里面的实现了

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 安卓圈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档