OKHttp详解
2016-06-07 19:22
519 查看
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()方法-(同步)。需要注意几点:
onResponse回调的参数是response,一般情况下,比如我们希望获得返回的字符串,可以通过
response.body().string()获取;如果希望获得返回的二进制字节数组,则调用
response.body().bytes();如果你想拿到返回的inputStream,则调用
response.body().byteStream()。(有inputStream我们就可以通过IO的方式写文件。)由此我们可以看出,onResponse方法执行的线程并不是UI线程。
okhttp支持异步的方式去执行,也支持阻塞的方式,直接调用
call.execute()通过返回一个
Response即为阻塞方式执行。
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
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件