okhttp3框架学习

这篇文章是参考网上一些优秀文章学习okhttp3的笔记,属于入门级,理解还不是很透彻。

http://www.cnblogs.com/qlky/p/7246331.html

根据Okhttp的使用步骤分析

步骤1.OkHttpClient okHttpClient = new OkHttpClient();

创建OkHttpClient对象。OkHttpClient为网络请求的一个中心,它会管理连接池、缓存、SocketFactory、代理、各种超时时间、DNS、请求的执行结果的分发等内容。

public OkHttpClient() {
  this(new Builder());
}
OkHttpClient(Builder builder) {
  this.dispatcher = builder.dispatcher;
  this.proxy = builder.proxy;
  this.protocols = builder.protocols;
  this.connectionSpecs = builder.connectionSpecs;
  this.interceptors = Util.immutableList(builder.interceptors);
  this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
  this.eventListenerFactory = builder.eventListenerFactory;
  this.proxySelector = builder.proxySelector;
  this.cookieJar = builder.cookieJar;
  this.cache = builder.cache;
  this.internalCache = builder.internalCache;
  this.socketFactory = builder.socketFactory;
                .
                .
                .
  this.connectionPool = builder.connectionPool;
  this.dns = builder.dns;
  this.followSslRedirects = builder.followSslRedirects;
  this.followRedirects = builder.followRedirects;
  this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
  this.connectTimeout = builder.connectTimeout;
  this.readTimeout = builder.readTimeout;
  this.writeTimeout = builder.writeTimeout;
  this.pingInterval = builder.pingInterval;
}

创建OkHttpClient就是对以上信息进行初始化,

也可以创建定制化的实例:

OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .connectTimeout(60, TimeUnit.SECONDS)
        .readTimeout(60,TimeUnit.SECONDS)
        .writeTimeout(60,TimeUnit.SECONDS)
        .retryOnConnectionFailure(true)
        .build()

其中Builder是OkHttpClient的一个内部的终态类

public static final class Builder

步骤2.Request request = new Request.Builder()
        .url(imageUrl)
        .method("GET", null)
        .build();

创建一个Request对象。Request用于描述一个HTTP请求,例如请求的方法是GET还是POST,请求的URL,请求的header,请求的body,请求的缓存策略等。

public Builder() {
  this.method = "GET";
  this.headers = new Headers.Builder();
}

如果不做设置,默认为get方法。

步骤3.Call call = okHttpClient.newCall(request);

利用前面创建的OkHttpClient对象和request对象创建Call对象。

public Call newCall(Request request) {
  return new RealCall(this, request, false /* for web socket */);
}
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
  final EventListener.Factory eventListenerFactory = client.eventListenerFactory();

  this.client = client;
  this.originalRequest = originalRequest;
  this.forWebSocket = forWebSocket;
  this.retryAndFollowUpInterceptor = new  RetryAndFollowUpInterceptor(client, forWebSocket);

  // TODO(jwilson): this is unsafe publication and not threadsafe.
  this.eventListener = eventListenerFactory.create(this);
}

这里实际上是返回了一个RealCall对象。

步骤4.Response execute = call.execute();call.enqueue(new Callback() {……});

Call是一次Http请求的Task,它会执行网络请求以获得响应。

OkHttp中的网络请求执行Call可以是同步,也可以是异步。调用call.execute()将直接执行网络请求,阻塞直到获得响应。而调用call.enqueue()传入回调,则会将Call放入一个异步执行的队列,由ExecutorService在后台执行。

//同步方法
public Response execute() throws IOException {
  synchronized (this) {

//这里是为了避免多次触发
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }

//设置调用堆栈跟踪
  captureCallStackTrace();
  try {
    client.dispatcher().executed(this); //注解1
    Response result = getResponseWithInterceptorChain();//注解2
    if (result == null) throw new IOException("Canceled");
    return result;
  } finally {
    client.dispatcher().finished(this);
  }
}
注解1:这里是将call添加到runningSyncCalls队列中

/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
  runningSyncCalls.add(call);
}
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

注解2:这里是okhttp请求获得responds的重点

Response getResponseWithInterceptorChain() throws IOException {
  // Build a full stack of interceptors.
  List<Interceptor> interceptors = new ArrayList<>();

//client为OkHttpClient对象,如果在初始化时并没有通过builder定制interceptor,则这里为一个size=0的集合。
  interceptors.addAll(client.interceptors());

//重定向拦截器
  interceptors.add(retryAndFollowUpInterceptor);

//桥接拦截器
  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, null, null, null, 0, originalRequest);

//获取Response
  return chain.proceed(originalRequest);
}

接下来看一下proceed()方法

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
    RealConnection connection) throws IOException {
  if (index >= interceptors.size()) throw new AssertionError();

  calls++;

  // If we already have a stream, confirm that the incoming request will use it.
  if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
    throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
        + " must retain the same host and port");
  }

  // If we already have a stream, confirm that this is the only call to chain.proceed().
  if (this.httpCodec != null && calls > 1) {
    throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
        + " must call proceed() exactly once");
  }

  // Call the next interceptor in the chain.
  RealInterceptorChain next = new RealInterceptorChain(
      interceptors, streamAllocation, httpCodec, connection, index + 1, request);
  Interceptor interceptor = interceptors.get(index);
  Response response = interceptor.intercept(next);

  // Confirm that the next interceptor made its required call to chain.proceed().
  if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
    throw new IllegalStateException("network interceptor " + interceptor
        + " must call proceed() exactly once");
  }

  // Confirm that the intercepted response isn't null.
  if (response == null) {
    throw new NullPointerException("interceptor " + interceptor + " returned null");
  }

  return response;
}
RealInterceptorChain next = new RealInterceptorChain(
      interceptors, streamAllocation, httpCodec, connection, index + 1, request);
  Interceptor interceptor = interceptors.get(index);
  Response response = interceptor.intercept(next);

此段代码是使拦截器连接下去的关键,当没有自定义的拦截器时,第一个是重定向拦截器,它的intercept()方法中有如下语句:

try {
  response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
  releaseConnection = false;
}

这里会再次调用proceed的方法。通过把index当做迭代指针控制,按列表顺序依次向下调用。

同样的BridgeInterceptor、CacheInterceptorConnectInterceptor中都有proceed()方法,使链连接下去。但是CallServerInterceptor作为最后一个连接器并没有调用这个方法。

RetryAndFollowUpInterceptor

重试与重定向拦截器,用来实现重试和重定向功能,内部通过while(true)死循环来进行重试获取Response(有重试上限,超过会抛出异常)。followUpRequest主要用来根据响应码来判断属于哪种行为触发的重试和重定向(比如未授权,超时,重定向等),然后构建响应的Request进行下一次请求。当然,如果没有触发重新请求就会直接返回Response。

RetryAndFollowUpInterceptor在intercept()中首先从client取得connection pool,用所请求的URL创建Address对象,并以此创建StreamAllocation对象。

RetryAndFollowUpInterceptor对重定向的响应也不会无休止的处理下去,它处理的最多的重定向级数为20次,超过20次时,它会抛异常出来。

总结一下RetryAndFollowUpInterceptor做的事情:

  1. 创建StreamAllocation对象,为后面流程的执行准备条件。
  2. 处理重定向的HTTP响应。
  3. 错误恢复。

BridgeInterceptor

桥接拦截器,用于完善请求头

这个Interceptor做的事情比较简单。可以分为发送请求和收到响应两个阶段来看。在发送请求阶段,BridgeInterceptor补全一些http header,这主要包括Content-Type、Content-Length、Transfer-Encoding、Host、Connection、Accept-Encoding、User-Agent,还加载Cookie,随后创建新的Request,并交给后续的Interceptor处理,以获取响应。

而在从后续的Interceptor获取响应之后,会首先保存Cookie。如果服务器返回的响应的content是以gzip压缩过的,则会先进行解压缩,移除响应中的header Content-Encoding和Content-Length,构造新的响应并返回;否则直接返回响应。

CacheInterceptor

缓存拦截器,首先根据Request中获取缓存的Response,然后根据用于设置的缓存策略来进一步判断缓存的Response是否可用以及是否发送网络请求(CacheControl.FORCE_CACHE因为不会发送网络请求,所以networkRequest一定为空)。如果从网络中读取,此时再次根据缓存策略来决定是否缓存响应。

对于CacheInterceptor.intercept(Chain chain)的分析同样可以分为两个阶段,即请求发送阶段和响应获取之后的阶段。这两个阶段由chain.proceed(networkRequest)来分割。

在请求发送阶段,主要是尝试从cache中获取响应,获取成功的话,且响应可用未过期,则响应会被直接返回;否则通过后续的Interceptor来从网络获取,获取到响应之后,若需要缓存的,则缓存起来。

关于HTTP具体的缓存策略这里暂时不再详述。

由RealCall.getResponseWithInterceptorChain()可见CacheInterceptor的cache同样来自于OkHttpClient。OkHttp已经有实现Cache的整套策略,在Cache类,但默认情况下不会被用起来,需要自己在创建OkHttpClient时,手动创建并传给OkHttpClient.Builder。

 

ConnectInterceptor

 连接拦截器,用于打开一个连接到远程服务器。说白了就是通过StreamAllocation获取HttpStream和RealConnection对象,以便后续读写。

CallServerInterceptor

调用服务拦截器,拦截链中的最后一个拦截器,通过网络与调用服务器。通过HttpStream依次次进行写请求头、请求头(可选)、读响应头、读响应体。

CallServerInterceptor首先将http请求头部发给服务器,如果http请求有body的话,会再将body发送给服务器,继而通过httpStream.finishRequest()结束http请求的发送。

随后便是从连接中读取服务器返回的http响应,并构造Response。

如果请求的header或服务器响应的header中,Connection值为close,CallServerInterceptor还会关闭连接。

 

 

总结一下这几个Interceptor的职责:
RetryAndFollowUpInterceptor --->创建StreamAllocation对象,处理http的redirect,出错重试。对后续Interceptor的执行的影响:修改request及StreamAllocation。
BridgeInterceptor-------------->补全缺失的一些http header。对后续Interceptor的执行的影响:修改request。
CacheInterceptor-------------->处理http缓存。对后续Interceptor的执行的影响:若缓存中有所需请求的响应,则后续Interceptor不再执行。
ConnectInterceptor------------>借助于前面分配的StreamAllocation对象建立与服务器之间的连接,并选定交互所用的协议是HTTP 1.1还是HTTP 2。对后续Interceptor的执行的影响:创建了httpStream和connection。
CallServerInterceptor----------->处理IO,与服务器进行数据交换。对后续Interceptor的执行的影响:为Interceptor链中的最后一个Interceptor,没有后续Interceptor。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值