zoukankan      html  css  js  c++  java
  • 源码系列--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请求还是差不多的,关键的就是各个拦截器里面的实现了

    欢迎关注我的微信公众号:安卓圈

  • 相关阅读:
    基于范围的for循环
    ML.NET技术研究系列-2聚类算法KMeans
    SQLServer常用运维SQL整理
    ML.NET技术研究系列-1入门篇
    Kafka基本知识整理
    .NetCore技术研究-EntityFramework Core 3.0 Preview
    容器技术研究-Kubernetes基本概念
    特来电混沌工程实践-混沌事件注入
    .Net Core技术研究-Span<T>和ValueTuple<T>
    Visual Studio Git本地Repos和GitHub远程Repos互操作
  • 原文地址:https://www.cnblogs.com/anni-qianqian/p/12796800.html
Copyright © 2011-2022 走看看