OKHttp详解

来源:互联网 发布:小学语文网络课程 编辑:程序博客网 时间:2024/05/29 09:35

OkHttp : Java和Android 高效http库,支持SPDY

http是现在主流应用使用的网络请求方式, 用来交换数据和内容, 有效的使用HTTP可以使你的APP 变的更快和减少流量的使用

OkHttp 是一个很棒HTTP客户端:

支持SPDY, 可以合并多个到同一个主机的请求

 使用连接池技术减少请求的延迟(如果SPDY是可用的话)

 使用GZIP压缩减少传输的数据量

 缓存响应避免重复的网络请求

当你的网络出现拥挤的时候,就是OKHttp 大显身手的时候, 它可以避免常见的网络问题,如果你的服务是部署在不同的IP上面的,如果第一个连接失败, OkHTtp会尝试其他的连接. 这个对现在IPv4+IPv6 中常见的把服务冗余部署在不同的数据中心上.  OkHttp 将使用现在TLS特性(SNI ALPN) 来初始化新的连接. 如果握手失败, 将切换到SLLv3使用OkHttp很容易,   同时支持 异步阻塞请求和回调.

okhttp主要有路由、连接协议、拦截器、代理、安全性认证、连接池以及网络适配,拦截器主要是添加,移除或者转换请求或者

回应的头部信息,总流程图如下:


okhttp的使用


使用前,对于Android Studio的用户,可以选择添加:

compile 'com.squareup.okhttp:okhttp:2.4.0'

或者Eclipse的用户,可以下载最新的jar okhttp he latest JAR ,添加依赖就可以用了。

注意:okhttp内部依赖okio,别忘了同时导入okio:

gradle: compile 'com.squareup.okio:okio:1.5.0'

okhttp可实现以下几种功能:

  • 一般的get请求
  • 一般的post请求
  • 基于Http的文件上传
  • 文件下载
  • 加载图片
  • 支持请求回调,直接返回对象、对象集合
  • 支持session的保持

httpGet:

    //创建okHttpClient对象     OkHttpClient mOkHttpClient = new OkHttpClient();    //创建一个Request      final Request request = new Request.Builder()         .url("https://github.com/Android")         //添加头。在使用时头加不加取决于你用不用        .header("User-Agent", "OkHttp Headers.java")        .addHeader("Accept", "application/json; q=0.5")        .addHeader("Accept", "application/vnd.github.v3+json").build();     //通过request的对象去构造得到一个Call对象(类似于将你的请求封装成了任务,既然是任务,就会有execute()和cancel()等方法。)     Call call = mOkHttpClient.newCall(request);     //请求加入调度   //以异步的方式去执行请求,这里是将call加入调度队列  call.enqueue(new Callback()        {            @Override            public void onFailure(Request request, IOException e){//请求失败返回对应的请求,及造成失败的异常            }            @Override            public void onResponse(final Response response) throws IOException //请求成功返回对应的响应            {                    //String htmlStr =  response.body().string();            }        });//同步get和异步get的的区别是这里的call调用的是enqueue()方法-(异步)还是excute()方法-(同步)。
需要注意几点:

  1. onResponse回调的参数是response,一般情况下,比如我们希望获得返回的字符串,可以通过response.body().string()获取;如果希望获得返回的二进制字节数组,则调用response.body().bytes();如果你想拿到返回的inputStream,则调用response.body().byteStream()。(有inputStream我们就可以通过IO的方式写文件。)由此我们可以看出,onResponse方法执行的线程并不是UI线程。
  2. okhttp支持异步的方式去执行,也支持阻塞的方式,直接调用call.execute()通过返回一个Response即为阻塞方式执行
  3. OkHttp官方文档并不建议我们创建多个OkHttpClient,因此全局使用一个。 如果有需要,可以使用clone方法,再进行自定义。

httpPost:

Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, null);FormEncodingBuilder builder = new FormEncodingBuilder();   builder.add("username","pamper"); //post请求的参数,可添加多个键值对。//构造Request对象Request request = new Request.Builder()                .url(url)                .post(builder.build())                .build(); mOkHttpClient.newCall(request).enqueue(new Callback(){});
如果是Json数据:

public static final MediaType JSON = MediaType.parse("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();      Response response = client.newCall(request).execute();    if (response.isSuccessful()) {        return response.body().string();    } else {        throw new IOException("Unexpected code " + response);    }}


基于Http的文件上传

File file = new File(Environment.getExternalStorageDirectory(), "balabala.mp4");/*构造请求体*/RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);/*构造请求体*/RequestBody requestBody = new MultipartBuilder()     .type(MultipartBuilder.FORM)      .addPart(Headers.of(          "Content-Disposition",               "form-data; name=\"username\""),           RequestBody.create(null, "PAMPER"))   //键值对做参数     .addPart(Headers.of(           "Content-Disposition",          "form-data; name=\"mFile\";          filename=\"wjd.mp4\""), fileBody) //文件作为传送的参数     .build();Request request = new Request.Builder()    .url("http://192.168.1.103:8080/okHttpServer/fileUpload")    .post(requestBody)    .build();Call call = mOkHttpClient.newCall(request);call.enqueue(new Callback(){    //...});

这里其实类似于拼接模拟浏览器行为的方式,如果你对这块不了解,可以参考:http://blog.csdn.net/lmj623565791/article/details/23781773

图片下载:通过回调的Response拿到byte[]然后decode成图片

文件下载:就是拿到inputStream做写文件操作

具体实现参考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0106/2275.html  个人这篇博文觉得写得很详细,就没有粘过来


okhttp源码解析

OkHttpClient:

源码中有一段英文介绍了他的用途:

which can be used to send HTTP requests and read their responses. Most applications can use a single OkHttpClient for all of their HTTP requests,benefiting from a shared response cache, thread pool, connection re-use, etc.

这段话的意思是 OkHttpClient是用来发送和接收请求的类,大多数应用使用一个OkHttpClient来进行HTTP请求,这得益于他共享的响应缓存,线程池、和连接复用等等。

在这个类中以下几个属性比较重要:

final Dispatcher dispatcher //分发器
final interceptors;//截获器final List<Interceptor> List<Interceptor> networkInterceptors; //截获器=对请求进行增删改的操作
final int connectTimeout;//建立TCP连接的时候多长时间没回复final int readTimeout;final int writeTimeout;
主要是构造一些东西,业务逻辑较少,大部分业务逻辑由底层实现

public static final class Builder {.......
public Builder() {  dispatcher = new Dispatcher();   ......
  connectTimeout = 10_000;  readTimeout = 10_000;  writeTimeout = 10_000;}
//从这里我们可以看出,在无参构造器中 不管是连接还是读写,默认的时间为10秒

//你也可以使用自己定义的okHttpClient去设置相关的参数,如连接等超时的时间

Builder(OkHttpClient okHttpClient) { this.dispatcher = okHttpClient.dispatcher;
...
this.connectTimeout = okHttpClient.connectTimeout; this.readTimeout = okHttpClient.readTimeout; this.writeTimeout = okHttpClient.writeTimeout;}


//另外 okHttpClient 的静态内部类 Builder里提供了设置这些属性的方法。如:
/** * Sets the default connect timeout for new connections. A value of 0 means no timeout, * otherwise values must be between 1 and {@link Integer#MAX_VALUE} when converted to * milliseconds. */public Builder connectTimeout(long timeout, TimeUnit unit) {
if (timeout < 0) throw new IllegalArgumentException("timeout < 0");if (unit == null) throw new IllegalArgumentException("unit == null");long millis = unit.toMillis(timeout);if (millis > Integer.MAX_VALUE) throw new IllegalArgumentException("Timeout too large.");if (millis == 0 && timeout > 0) throw new IllegalArgumentException("Timeout too small.");connectTimeout = (int) millis;return this;
}

Call:

打开Call.java首先映入眼帘的是:

/** * A call is a request that has been prepared for execution. A call can be canceled. As this object * represents a single request/response pair (stream), it cannot be executed twice. */
这句话清楚地解释了Call这个类的特点,他是已经准备执行的请求,他可以被取消。由于这个对象(Call对象)代表一个单一的请求/响应对(流),它不能被执行两次。


Call是一个接口,他拥有一个内部接口,这个接口提供两了Call的实现类对象的生成方法

interface Factory {  Call newCall(Request request);}


另两个重要的方法如下:

/** *  Invokes the request immediately, and blocks until the response can be processed or is in
 * error.
 * 立即执行请求,并且进入阻塞状态,直到上一个请求被执行完或者执行出错。
*/Response execute() throws IOException; //同步的/** * Schedules the request to be executed at some point in the future. * 计划在将来某个时候执行的请求:不马上执行,把他放在一个队列里。 */void enqueue(Callback responseCallback); //异步的

RealCall:Call的实现类。

final class RealCall implements Call {}//首先我们看一下execute()方法的实现:
@Override public Response execute() throws IOException {  synchronized (this) { //同步锁,线程同步。。。当出锁之后别的才可以进来。    if (executed) throw new IllegalStateException("Already Executed");    executed = true;  try {    client.dispatcher().executed(this);    Response result = getResponseWithInterceptorChain(false);//可以有很多截获器,然后call会依次的遍历截获器,然后把//对应的request返回回来。 if (result == null) throw new IOException("Canceled");    return result;  } finally {    client.dispatcher().finished(this);  }}


Dispatcher里面excute方法的实现如下:

分发器只对异步起作用,同步的不起作用。

/** Used by {@code Call#execute} to signal it is in-flight. */synchronized void executed(RealCall call) {  runningSyncCalls.add(call);}从这我们可以看出,这里并不是真的执行了excute方法,而是将RealCall的对象加入Deque

/** Running synchronous calls. Includes canceled calls that haven't finished yet. */private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
public interface Deque<E> extends Queue<E> {
...}
由此我们可以看出Deque是一个队列。
getResponseWithInterceptorChain:

private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {/**三个参数分别为:索引,请求,socket*/ Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);//应用层的截获器链条  return chain.proceed(originalRequest);//}

  @Override public Response proceed(Request request) throws IOException {    // If there's another interceptor in the chain, call that.    if (index < client.interceptors().size()) {      Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);      Interceptor interceptor = client.interceptors().get(index);//通过索引识别第几个加进来的。//也就是通过索引区分截获器.这里虽然new了一个新的,但是他启用的上一个截获器。      Response interceptedResponse = interceptor.intercept(chain);      if (interceptedResponse == null) {        throw new NullPointerException("application interceptor " + interceptor            + " returned null");      }      return interceptedResponse;//如果还有就返回对应的响应    }    // No more interceptors. Do HTTP.    return getResponse(request, forWebSocket); //说明遍历完成了。此时发送网络请求。  }}
发送网络请求的代码如下:

/** * Performs the request and returns the response. May return null if this call was canceled. */Response getResponse(Request request, boolean forWebSocket) throws IOException {  // Copy body metadata to the appropriate request headers.  RequestBody body = request.body();//构造请求体//---- 与HTTP协议相关-------------------------------------------//if (body != null) {    Request.Builder requestBuilder = request.newBuilder();    MediaType contentType = body.contentType();    if (contentType != null) {      requestBuilder.header("Content-Type", contentType.toString());    }    long contentLength = body.contentLength();    if (contentLength != -1) {      requestBuilder.header("Content-Length", Long.toString(contentLength));      requestBuilder.removeHeader("Transfer-Encoding");    } else {      requestBuilder.header("Transfer-Encoding", "chunked");      requestBuilder.removeHeader("Content-Length");    }    request = requestBuilder.build();  }
//---- 与HTTP协议相关-------------------------------------//
 // Create the initial HTTP engine. Retries and redirects need new engine for each attempt.
//真正开始和网络打交道
HttpEngine的作用:处理网络拦截器、管理请求发送和回复
 
engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);
int
followUpCount = 0; while (true) {
if (canceled) {//如果请求取消了,释放 StreamAllocation 并抛出异常
engine.releaseStreamAllocation(); throw new IOException("Canceled");
}
boolean releaseConnection = true;
try
{
engine.sendRequest();//发送请求
engine.readResponse(); //读取响应
releaseConnection = false;
} catch (RequestException e) {
// The attempt to interpret the request failed. Give up.
throw e.getCause();
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null);
if
(retryEngine != null) {
releaseConnection = false;
engine = retryEngine;
  continue;
}
// Give up; recovery is not possible.
throw e.getLastConnectException();
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
HttpEngine retryEngine = engine.recover(e, null);
if
(retryEngine != null) {
releaseConnection = false;
engine
= retryEngine;
continue;

}
// Give up; recovery is not possible.
throw e;
} finally {
// We're throwing an unchecked exception. Release any resources.

if
(releaseConnection) {
StreamAllocation streamAllocation = engine.close(); //关闭engine
//StreamAllocation的作用:申请和释放连接资源--他协调了 Connection、Streams及Call。下面简要的分析了其代码。

streamAllocation.release(); //释放资源 } }
Response response = engine.getResponse();
Request followUp = engine.followUpRequest();
if
(followUp == null) {
if (!forWebSocket) {
engine
.releaseStreamAllocation();
} return response;
}
StreamAllocation streamAllocation = engine.close();
if
(++followUpCount > MAX_FOLLOW_UPS) { //请求数超出上线
streamAllocation.release();
throw new
ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (!engine.sameConnection(followUp.url())) {
streamAllocation.release();
streamAllocation = null;
}
request = followUp;
engine
= new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null, response);
}
}

Request.java

public final class Request {  private final HttpUrl url;  private final String method;//  get/post/delete等等  private final Headers headers; //请求头  private final RequestBody body; //请求体  private final Object tag;  private volatile URI javaNetUri; // Lazily initialized.  private volatile CacheControl cacheControl; // Lazily initialized....


StreamAllocation.java

public final class StreamAllocation {  private final ConnectionPool connectionPool;  // State guarded by connectionPool.  private RouteSelector routeSelector; //线路选择器  private RealConnection connection; //连接  private HttpStream stream; //流... 


/** * Finds a connection and returns it if it is healthy. If it is unhealthy the process is repeated * until a healthy connection is found. */private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,    int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)    throws IOException, RouteException {  while (true) {    RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,        connectionRetryEnabled);    // If this is a brand new connection, we can skip the extensive health checks.    synchronized (connectionPool) {      if (candidate.successCount == 0) {        return candidate;      }    }    // Otherwise do a potentially-slow check to confirm that the pooled connection is still good.    if (candidate.isHealthy(doExtensiveHealthChecks)) {      return candidate;    }    connectionFailed(new IOException());  }}


/** * Returns a connection to host a new stream. This prefers the existing connection if it exists, * then the pool, finally building a new connection. */private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,    boolean connectionRetryEnabled) throws IOException, RouteException {  Route selectedRoute;  synchronized (connectionPool) {    if (released) throw new IllegalStateException("released");    if (stream != null) throw new IllegalStateException("stream != null");    if (canceled) throw new IOException("Canceled");    RealConnection allocatedConnection = this.connection;    if (allocatedConnection != null && !allocatedConnection.noNewStreams) {      return allocatedConnection;    }    // Attempt to get a connection from the pool.    RealConnection pooledConnection = Internal.instance.get(connectionPool, address, this);    if (pooledConnection != null) {      this.connection = pooledConnection;      return pooledConnection;    }    selectedRoute = route;  }  if (selectedRoute == null) {    selectedRoute = routeSelector.next();    synchronized (connectionPool) {      route = selectedRoute;    }  }  RealConnection newConnection = new RealConnection(selectedRoute);  acquire(newConnection);  synchronized (connectionPool) {    Internal.instance.put(connectionPool, newConnection);    this.connection = newConnection;    if (canceled) throw new IOException("Canceled");  }  newConnection.connect(connectTimeout, readTimeout, writeTimeout, address.connectionSpecs(),      connectionRetryEnabled);  routeDatabase().connected(newConnection.route());  return newConnection;}public void streamFinished(boolean noNewStreams, HttpStream stream) {  synchronized (connectionPool) {    if (stream == null || stream != this.stream) {      throw new IllegalStateException("expected " + this.stream + " but was " + stream);    }    if (!noNewStreams) {      connection.successCount++;    }  }  deallocate(noNewStreams, false, true);}


/** * Releases resources held by this allocation. If sufficient resources are allocated, the * connection will be detached or closed. */private void deallocate(boolean noNewStreams, boolean released, boolean streamFinished) {  RealConnection connectionToClose = null;  synchronized (connectionPool) {    if (streamFinished) {      this.stream = null;    }    if (released) {      this.released = true;    }    if (connection != null) {      if (noNewStreams) {        connection.noNewStreams = true;      }      if (this.stream == null && (this.released || connection.noNewStreams)) {        release(connection);        if (connection.allocations.isEmpty()) {          connection.idleAtNanos = System.nanoTime();          if (Internal.instance.connectionBecameIdle(connectionPool, connection)) {            connectionToClose = connection;          }        }        connection = null;      }    }  }  if (connectionToClose != null) {    Util.closeQuietly(connectionToClose.socket());  }}

Connection.java

The sockets and streams of an HTTP, HTTPS, or HTTPS+SPDY connection. May be used for multiple HTTP request/response exchanges. Connections may be direct to the origin server or via a proxy.一个HTTP、HTTPS或者Https_SPDY连接的套接字和流,
可用于多个HTTP请求/响应的交流。连接可以直接到原始服务器或通过代理
Typically instances of this class are created, connected and exercised automatically by the HTTP client. 
Applications may use this class to monitor HTTP connections as members of a
{@linkplain ConnectionPool connection pool}.

通常情况下这个类的对象被创建后将会由Http 客户端自动连接和执行。应用程序可以使用这个类来监视HTTP连接为一个
“linkplain连接池连接池成员}。
public interface Connection {}
其实现类:RealConnection:

collection 的 noNewStream(鉴别这个connection能不能用,能不能进行流的读写)属性,如果为true表示:不能再加入流,他会自然而然的被连接池回收


HttpStream的作用:

一、与读写绑定

二、写与协议相关的header、body及与读协议相关的response。


HttpEngine.java


/** * Flushes the remaining request header and body, parses the HTTP response headers and starts * reading the HTTP response body if it exists. */public void readResponse() throws IOException {  if (userResponse != null) {    return; // Already ready.  }  if (networkRequest == null && cacheResponse == null) {    throw new IllegalStateException("call sendRequest() first!");  }  if (networkRequest == null) {    return; // No network response to read.  }  Response networkResponse;//这里有一个状态机: if (forWebSocket) {    httpStream.writeRequestHeaders(networkRequest);    networkResponse = readNetworkResponse();  } else if (!callerWritesRequestBody) { //callerWritesRequestBody 通常都为false//创建网络应用层的截获器链条。 networkResponse = new NetworkInterceptorChain(0, networkRequest).proceed(networkRequest);  } else {    // Emit the request body's buffer so that everything is in requestBodyOut.    if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) {      bufferedRequestBody.emit();    }    // Emit the request headers if we haven't yet. We might have just learned the Content-Length.    if (sentRequestMillis == -1) {      if (OkHeaders.contentLength(networkRequest) == -1          && requestBodyOut instanceof RetryableSink) {        long contentLength = ((RetryableSink) requestBodyOut).contentLength();        networkRequest = networkRequest.newBuilder()            .header("Content-Length", Long.toString(contentLength))            .build();      }//写网络请求--写入socket。 httpStream.writeRequestHeaders(networkRequest);    }    // Write the request body to the socket.    if (requestBodyOut != null) {      if (bufferedRequestBody != null) {        // This also closes the wrapped requestBodyOut.        bufferedRequestBody.close();      } else {        requestBodyOut.close();      }      if (requestBodyOut instanceof RetryableSink) {        httpStream.writeRequestBody((RetryableSink) requestBodyOut);      }    }    networkResponse = readNetworkResponse();  }  receiveHeaders(networkResponse.headers());  // If we have a cache response too, then we're doing a conditional get.  if (cacheResponse != null) {    if (validate(cacheResponse, networkResponse)) {      userResponse = cacheResponse.newBuilder()          .request(userRequest)          .priorResponse(stripBody(priorResponse))          .headers(combine(cacheResponse.headers(), networkResponse.headers()))          .cacheResponse(stripBody(cacheResponse))          .networkResponse(stripBody(networkResponse))          .build();      networkResponse.body().close();      releaseStreamAllocation();      // Update the cache after combining headers but before stripping the      // Content-Encoding header (as performed by initContentStream()).      InternalCache responseCache = Internal.instance.internalCache(client);      responseCache.trackConditionalCacheHit();      responseCache.update(cacheResponse, stripBody(userResponse));      userResponse = unzip(userResponse);      return;    } else {      closeQuietly(cacheResponse.body());    }  }  userResponse = networkResponse.newBuilder()      .request(userRequest)      .priorResponse(stripBody(priorResponse))      .cacheResponse(stripBody(cacheResponse))      .networkResponse(stripBody(networkResponse))      .build();  if (hasBody(userResponse)) {    maybeCache();    userResponse = unzip(cacheWritingResponse(storeRequest, userResponse));  }}HttpStream.java 

public interface HttpStream {
...

public final class Http1xStream implements HttpStream {//-------------------------状态机的几种状态----------------------------------------- private static final int STATE_IDLE = 0; // Idle connections are ready to write request headers.  private static final int STATE_OPEN_REQUEST_BODY = 1;//打开请求体  private static final int STATE_WRITING_REQUEST_BODY = 2;//写请求体  private static final int STATE_READ_RESPONSE_HEADERS = 3;//读请求头  private static final int STATE_OPEN_RESPONSE_BODY = 4;//打开响应体  private static final int STATE_READING_RESPONSE_BODY = 5;//正在读响应体  private static final int STATE_CLOSED = 6;//关闭 
//-------------------------状态机的几种状态-----------------------------------------

/** * 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 {  httpEngine.writingRequestHeaders();  String requestLine = RequestLine.get(      request, httpEngine.getConnection().route().proxy().type());  writeRequest(request.headers(), requestLine);}


网络截获器与应用层截获器的区别:

网络截获器:由于会处理http请求的重定位,每个interceptor会截获几次和http 服务器的request/response

应用层截获器:每个interceptor只会截获一次


ConnectionPool.java

/** * Manages reuse of HTTP and SPDY connections for reduced network latency. HTTP requests that share * the same {@link Address} may share a {@link Connection}. This class implements the policy of * which connections to keep open for future use.管理HTTP和SPDY连接复用,减少网络延迟。HTTP请求共享同一{@链接地址}或者共享一个{ @链路连接}。这个类实现了为将来的使用保持打开状态 的策略。 */public final class ConnectionPool {
private final long keepAliveDurationNs;//保持活跃状态一些时间
/** * Prunes any leaked allocations and then returns the number of remaining live allocations on * {@code connection}. Allocations are leaked if the connection is tracking them but the * application code has abandoned them. Leak detection is imprecise and relies on garbage * collection.
清理虽有的泄露的配置并返回剩余的活分配{ @代码连接数}。如果连接是跟踪他们,但应用程序代码已经放弃了他们,那么这种
分配时泄露的。对于泄漏的检测是不精确的,他依赖于垃圾收
*/ private int pruneAndGetAllocationCount(RealConnection connection, long now) { List<Reference<StreamAllocation>> references = connection.allocations; for (int i = 0; i < references.size(); ) { Reference<StreamAllocation> reference = references.get(i); if (reference.get() != null) { //统计正常的分配的个数 i++; continue; } // We've discovered a leaked allocation. This is an application bug. Internal.logger.warning("A connection to " + connection.route().address().url() + " was leaked. Did you forget to close a response body?"); references.remove(i); connection.noNewStreams = true; //将连接设为不能加入流,我们在前面提到,noNewStream为true 的链接会被回收 // 如果这是最后的配置,连接应立即驱逐 if (references.isEmpty()) { connection.idleAtNanos = now - keepAliveDurationNs;//空闲时间为五分钟之前,那么就是说你的空闲时间已经被用完
//了,就是说这应该被移除了
 
return 0; } } return references.size(); }}



参考文章:

http://blog.csdn.net/lmj623565791/article/details/4791108


0 0