# Okhttp

Okhttp

一、基本用法

以post请求为例,基本用法如下:

	  OkHttpClient client = new OkHttpClient.Builder().build();
	  FormBody body = new FormBody.Builder()
	          .add("phone","1762192344")
	          .add("passwd","1ass21")
	          .build();
	  Request request = new Request
	          .Builder()
	          .post(body)
	          .url("http://xxxxxxxx/login")
	          .build();
	
	  Call call = client.newCall(request);
	  Response execute = null;
	  try {
//同步请求
	       execute = call.execute();
// 异步请求
//            call.enqueue(new Callback() {
//                @Override
//                public void onFailure(Call call, IOException e) {
//
//                }
//
//                @Override
//                public void onResponse(Call call, Response response) throws IOException {
//
//                }
//            });
	       Log.d(TAG, "okHttp3: "+execute.body().string());
	  } catch (IOException e) {
	       e.printStackTrace();
	  }

可以看到大致分四步:
1、构建OkHttpClient对象;
2、构建Request请求对象;
3、构建Call对象;
4、发起请求;
下面一步一步分析

二、4步逐步分析

1.构建OkHttpClient对象

OkHttpClient采用建造者模式创建对象,源码如下:
在这里插入图片描述
这就是一个标准的构建者模式,将http请求的一些配置封装到client对象中。
可以看到里面有分发器,拦截器,超时设置等。

2.构建Request请求对象

在这里插入图片描述
这也是一个建造者模式把请求的url、method、header全部封装到Request对象中。

3.创建Call对象

先来看看Call,这是一个接口,定义了一些request()、enqueue(Callback responseCallback)、execute()等方法,其实现类是RealCall,先搁置一边。回过头来看client.newCall(request)跟进代码,查看newCall(Request request)方法。
OkHttpClient类:
在这里插入图片描述
RealCall类:
在这里插入图片描述
果然,这里就只是跟进传进来的Request和当前的client对象创建了一个RealCall对象,也就是说使用方法中的第三步(Call call = client.newCall(request))执行完成后,得到了一个ReallCall对象,接下来到了第四步。

4. 发起请求

先看execute = call.execute()请求
a).同步请求execute()
在这里插入图片描述
关键代码为标出来的注释1和注释2两处,先看注释1,看下client.dispatcher()返回的Dispatcher对象:
在这里插入图片描述
这是一个调度器,内部维护着最大请求数,每个主机最大请求数等参数,最重要的是维护着三个队列,分别是“将要执行的异步请求队列”、“正在执行的异步请求队列”和“正在执行的同步执行队列”。之前的代码段中注释1处调用dispatcher.executed(this)方法,我们看到这个方法只是把当前的realCall实例加入到了正在执行的同步请求队列中。接下来看注释2处的代码Response result = getResponseWithInterceptorChain(),看下这个方法:
在这里插入图片描述
在这个方法中,将用户自定义的一些拦截器和默认的拦截器封装到一个list中,然后创建RealInterceptorChain对象并执行proceed(originalRequest)方法,接下来将是重点。看一下这个方法

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    //...省去异常处理...
	
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request, call,eventListener, connectTimeout, readTimeout,writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    //...省去异常处理...

    return response;
  }

关键代码只有三行,这里会遍历调用拦截器列表中的拦截器,并调用每一个拦截器的intercept(RealInterceptorChain chain)方法,先看这里的第一个拦截器RetryAndFollowUpInterceptor的intercept(Chain chain)方法

@Override 
public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    //...省略部分代码....
    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
      //...省略异常处理代码...

      Response response;
      boolean releaseConnection = true;
      try {
      	//注释3
        response = realChain.proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
          throw e.getFirstConnectException();
        }
        releaseConnection = false;
        continue;
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
        releaseConnection = false;
        continue;
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
          streamAllocation.streamFailed(null);
          streamAllocation.release(true);
        }
      }

      // Attach the prior response if it exists. Such responses never have a body.
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }

      Request followUp;
      try {
        followUp = followUpRequest(response, streamAllocation.route());
      } catch (IOException e) {
        streamAllocation.release(true);
        throw e;
      }

      if (followUp == null) {
        streamAllocation.release(true);
        return response;
      }

      closeQuietly(response.body());

      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release(true);
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      if (followUp.body() instanceof UnrepeatableRequestBody) {
        streamAllocation.release(true);
        throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
      }

      if (!sameConnection(response, followUp.url())) {
        streamAllocation.release(false);
        streamAllocation = new StreamAllocation(client.connectionPool(),
            createAddress(followUp.url()), call, eventListener, callStackTrace);
        this.streamAllocation = streamAllocation;
      } else if (streamAllocation.codec() != null) {
        throw new IllegalStateException("Closing the body of " + response
            + " didn't close its backing stream. Bad interceptor?");
      }

      request = followUp;
      priorResponse = response;
    }
  }

在一个while(true)中执行,当重连次数followUpCount超过20次的时候,停止重连。当执行到注释3的时候,又会重新执行RealInterceptorChain.proceed()方法。但是这次会取出下一个拦截器BridgeInterceptor并执行它的intercept(Chain chain)方法。以这种方式就会遍历完拦截器list中的拦截器,并调用响应的intercept方法,接下来的几个拦截器的intercept方法比较简单,就不分析了。同样也会在此方法中调用realChain.proceed()方法进入下一个拦截器,直到最后一个拦截器CallServerInterceptor,看下它的intercept方法:

@Override public Response intercept(Chain chain) throws IOException {
    final RealInterceptorChain realChain = (RealInterceptorChain) chain;
    //...省略代码

    realChain.eventListener().requestHeadersStart(call);
   	//向服务端发起请求
    httpCodec.writeRequestHeaders(request);
    realChain.eventListener().requestHeadersEnd(call, request);

    Response.Builder responseBuilder = null;
    //如果Request请求的body不为空,需要包装RequestBody并发送
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      //...省略代码....

      if (responseBuilder == null) {
        if (request.body() instanceof DuplexRequestBody) {
          //...省略代码....
        } else {
          // Write the request body if the "Expect: 100-continue" expectation was met.
          realChain.eventListener().requestBodyStart(call);
          //获取body内容的长度
          long contentLength = request.body().contentLength();
          CountingSink requestBodyOut =
              new CountingSink(httpCodec.createRequestBody(request, contentLength));
          
          BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
         //发送RequestBody
          request.body().writeTo(bufferedRequestBody);
          bufferedRequestBody.close();
          realChain.eventListener().requestBodyEnd(call, requestBodyOut.successfulCount);
        }
      } else if (!connection.isMultiplexed()) {
        // ...省略代码....
      }
    }
	//完成请求
    if (!(request.body() instanceof DuplexRequestBody)) {
      httpCodec.finishRequest();
    }

    if (responseBuilder == null) {
      realChain.eventListener().responseHeadersStart(call);
      //读取返回的Response
      responseBuilder = httpCodec.readResponseHeaders(false);
    }
	//构建Response
    responseBuilder
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis());
    Internal.instance.initCodec(responseBuilder, httpCodec);
    Response response = responseBuilder.build();

    //...省略了Response的code==100的处理...

    if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
	  // 构建一个非空的Response返回
    } else {
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }
	//...关闭资源...
	//...异常处理...
    return response;
  }

下面详细分析一下这个方法:分析这个方法之前我们先看HttpCodec这个接口的介绍Encodes HTTP requests and decodes HTTP responses。编码http请求并解码http返回结果,也就是说真正的处理请求和结果的是这个接口,它有两个实现类分别是Http1Codec和Http2Codec分别对应Http/1.x和Http/2.x。
好了,开始继续之前的分析,首先通过httpCodec.writeRequestHeaders(request)发起请求,以Http1Codec为例

/**
   * Prepares the HTTP headers and sends them to the server.
   *
   * <p>For streaming requests with a body, headers must be prepared <strong>before</strong> the
   * output stream has been written to. Otherwise the body would need to be buffered!
   *
   * <p>For non-streaming requests with a body, headers must be prepared <strong>after</strong> the
   * output stream has been written to and closed. This ensures that the {@code Content-Length}
   * header field receives the proper value.
   */
  @Override public void writeRequestHeaders(Request request) throws IOException {
    String requestLine = RequestLine.get(
        request, streamAllocation.connection().route().proxy().type());
    writeRequest(request.headers(), requestLine);
  }

RealBufferedSink
看javadoc写明了Prepares the HTTP headers and sends them to the server.,读取请求行并调用writeRequest(request.headers(), requestLine)方法

/** Returns bytes of a request header for sending on an HTTP transport. */
  public void writeRequest(Headers headers, String requestLine) throws IOException {
    if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
    sink.writeUtf8(requestLine).writeUtf8("\r\n");
    for (int i = 0, size = headers.size(); i < size; i++) {
      sink.writeUtf8(headers.name(i))
          .writeUtf8(": ")
          .writeUtf8(headers.value(i))
          .writeUtf8("\r\n");
    }
    sink.writeUtf8("\r\n");
    state = STATE_OPEN_REQUEST_BODY;
  }

发现oKHttp通过okio的Sink对象(该对象可以看做Socket的OutputStream对象)的writeRequest来向服务器发送请求的(如想了解更多,参靠okio源码分析);

继续之前的分析,发送完header之后,如果请求有请求body,还需要发送请求bodyhttpCodec.createRequestBody(request, contentLength)创建了固定长度http bodyFixedLengthSink然后将请求body通过Okio.buffer()方法封装成RealBufferedSink对象,最后通过RequestBody的writeTo方法.

这里是RequestBody的子类FormBody的writeTo,写入了okio的Buffer中,请求完成调用httpCodec.finishRequest();,跟进内部发现调用了flush()方法,把‘流’都写到请求中,请求完成。但是服务器结果还没返回 ,我们继续。读取返回结果的代码,responseBuilder = httpCodec.readResponseHeaders(false);这段代码的解析来自一个http返回的header,

 @Override 
 public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
    //...省略代码....
    try {
      StatusLine statusLine = StatusLine.parse(readHeaderLine());

      Response.Builder responseBuilder = new Response.Builder()
          .protocol(statusLine.protocol)
          .code(statusLine.code)
          .message(statusLine.message)
          .headers(readHeaders());

      
      return responseBuilder;
    } catch (EOFException e) {
    
    }
  }

解析返回结果的header并构建Response.Builder对象,至于请求体在哪?接着往下看,拿到Response.Builder对象后就可以构建Response对象,然后通过httpCodec.openResponseBody(response)获取请求体,结合刚才获取Response生成一个新的Response返回。

至此整个流程就分析完了,但是这只是call.execute()同步执行的流程,那异步执行的流程是怎么样的呢?继续往下看
b).异步请求enqueue(Callback responseCallback)
与同步请求不同的是,异步请求执行的是RealCall的enqueue(Callback responseCallback)方法:

@Override 
public void enqueue(Callback responseCallback) {
    synchronized (this) {
      //只执行一次
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

可以看到这个方法最终生成了一个AsyncCall对象并执行了dispatcher的enqueue(AsyncCall call)接下来看下这个方法

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

前面我们说过Dispatcher维护着三个队列,两个异步的和一个同步的,此方法将异步请求加入到即将执行的异步队列中,然后开始执行

/**
   * Promotes eligible calls from {@link #readyAsyncCalls} to {@link #runningAsyncCalls} and runs
   * them on the executor service. Must not be called with synchronization because executing calls
   * can call into user code.
   *
   * @return true if the dispatcher is currently running calls.
   */
  private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      //遍历将要执行的异步请求队列
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();
		//正在执行异步请求队列已满,跳出循环
        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
		//从当前队列移除
        i.remove();
        asyncCall.callsPerHost().incrementAndGet();
        executableCalls.add(asyncCall);
        //将当前异步请求加进正在执行的队列中
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }

    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }

将准备队列中的请求取出来,如果满足条件就放到正在执行队列中,并调用这些请求的executeOn(executorService())方法,executorService()返回线程池,所以看executeOn()方法

/**
     * Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up
     * if the executor has been shut down by reporting the call as failed.
     */
    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);
        eventListener.callFailed(RealCall.this, ioException);
        responseCallback.onFailure(RealCall.this, ioException);
      } finally {
        if (!success) {
          client.dispatcher().finished(this); // This call is no longer running!
        }
      }
    }

调用线程池的execute(this)执行当前的call,若捕获异常就调用responseCallback.onFailure(RealCall.this, ioException);,先看一下AsyncCall这个类是什么,此类继承自NamedRunnable,而NamedRunnable是实现了Runnable接口并且有名字的线程类:

public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

当此线程被执行的时候,会调用execute()方法,也就会调用到子类AsyncCall的execute()方法,来看一下这个方法中做了什么

  @Override 
  protected void execute() {
      boolean signalledCallback = false;
      timeout.enter();
      try {
      	//注释4
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          //回调
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          //回调
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        e = timeoutExit(e);
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

看注释4,看到了我们之前分析过的代码,之后的流程就跟同步请求的顺序执器拦截器的代码逻辑一样了。至此okhttp3的同步和异步请求都执行完了。

总结

简单总结一下,okhttp3通过建造者模式构建OkHttpClient及Request,通过线程池执行异步请求,通过拦一系列截器与服务器建立连接、发送数据并解析返回结果。
用流程图表示如下:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值