1. 用法: 设置超时时间OkHttpClient httpClient = new OkHttpClient.Builder() .retryOnConnectionFailure(true) .connectTimeout(connect_TIMEOUT, TimeUnit.SECONDS) //连接超时 .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS) //读取超时 .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS) //写超时 .addInterceptor(getHeaderInterceptor()) .addInterceptor(new CacheInterceptor()) .addInterceptor(new HttpLoggerInterceptor() .setLevel(BuildConfig.DEBUG ? Level.BODY : Level.NONE) .setTag(HTTP_LOG_TAG)) .build(); ,今天小编就来聊一聊关于okhttp需要关闭连接吗?接下来我们就一起去研究一下吧!

okhttp需要关闭连接吗(okhttpTimeout超时设置与用法解释)

okhttp需要关闭连接吗

1. 用法: 设置超时时间

OkHttpClient httpClient = new OkHttpClient.Builder() .retryOnConnectionFailure(true) .connectTimeout(connect_TIMEOUT, TimeUnit.SECONDS) //连接超时 .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS) //读取超时 .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS) //写超时 .addInterceptor(getHeaderInterceptor()) .addInterceptor(new CacheInterceptor()) .addInterceptor(new HttpLoggerInterceptor() .setLevel(BuildConfig.DEBUG ? Level.BODY : Level.NONE) .setTag(HTTP_LOG_TAG)) .build();

这个都知道, 一搜一大把, 但是没人讲这三种timeout有什么区别...

2. 总结

源码分析之前先上总结

一言以蔽之: okhttp底层基于socket, 所以 Timeout 自然也是设置给�Socket 的 connect / read / write

当然, 不懂socket怎么用的最好先查一下~

以下的源码探究就是罗列记录以下自己的探究过程, 可以忽略~

3. 源码探究

3.1 设置给rawSocket 上的 connectTimeout 和 readTimeout

具体实现在 RealConnection这个类的connectSocket(,,,)方法

/** * Does all the work necessary to build a full HTTP or HTTPS connection on a raw socket. */ private void connectSocket(int connectTimeout, int readTimeout, Call call, EventListener eventListener) throws IOException { Proxy proxy = route.proxy(); address address = route.address(); //在未设置proxy的情况下, 会采用默认的proxySelector, 此时的proxy.type == DIRECT 即直连 rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP ? address.socketFactory().createSocket() // 走这里, 实际new Socket() : new Socket(proxy); eventListener.connectStart(call, route.socketAddress(), proxy); //最终调用socket.setSoTimeout方法, 设置读取server端数据的超时; rawSocket.setSoTimeout(readTimeout); try { //实际调用的是 rawSocket.connect(route.socketAddress(), connectTimeout), 设置连接server的超时时长 Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout); } catch (ConnectException e) { ... throw ce; } // The following try/catch block is a pseudo hacky way to get around a crash on Android 7.0 // More details: // https://github.com/square/okhttp/issues/3245 // https://android-review.googlesource.com/#/c/271775/ try { //创建source source = Okio.buffer(Okio.source(rawSocket)); //创建sink sink = Okio.buffer(Okio.sink(rawSocket)); } catch (NullPointerException npe) { if (NPE_THROW_WITH_NULL.equals(npe.getMessage())) { throw new IOException(npe); } } }

关于socket.setSoTimeout, 以下是原文档说明的个人翻译及理解

调用此方法设置一个非0的timeout,那么调用InputStream(与此Socket相关联的) 的read()这个阻塞方法读取server端的数据时, 持续timeout之久。

如果timeout 到期,不管Socket是否有效, 都会抛出java.net.SocketTimeoutException。

这个timeout 必须在socket进入block操作之前设置 才能生效;

正常设置timeout >0, 如果设置timeout=0, 则代表 timeout无限;

关于socket.connect(address, connectTimeout);

Connects this socket to the server with a specified timeout value. A timeout of zero is interpreted as an infinite timeout. The connection will then block until established or an error occurs.

简言之就是 与server建立连接的最大时长

3.2 BufferedSource上的 readTimeout 和 BufferedSink上的writeTimeout

image.png

具体是什么鬼, 看一下source和sink的创建就是知道了

BufferedSource的创建

罗列细节之前先总结一下流程:

Socket ----> InputStream ---> Source ---> BufferedSource

还是RealConnection的connectSocket方法

//创建BufferedSource source = Okio.buffer(Okio.source(rawSocket));

Okio.buffer(Source source)就是new RealBufferedSource(source);

那么下面主要来看Okio.source(rawSocket)

public static Source source(Socket socket) throws IOException { if (socket == null) throw new IllegalArgumentException("socket == null"); AsyncTimeout timeout = timeout(socket); //此处用socket的inputstream创建了source Source source = source(socket.getInputStream(), timeout); return timeout.source(source); } //下面请看 okio 是如何将 inputstream 封装成 source 的 private static Source source(final InputStream in, final Timeout timeout) { if (in == null) throw new IllegalArgumentException("in == null"); if (timeout == null) throw new IllegalArgumentException("timeout == null"); return new Source() { @Override public long read(Buffer sink, long byteCount) throws IOException { if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " byteCount); if (byteCount == 0) return 0; try { //每次read都会检测timeout timeout.throwIfReached(); Segment tail = sink.writableSegment(1); int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit); //本质还是调用了inputstream的read方法 int bytesRead = in.read(tail.data, tail.limit, maxToCopy); if (bytesRead == -1) return -1; tail.limit = bytesRead; sink.size = bytesRead; return bytesRead; } catch (AssertionError e) { if (isAndroidGetsocknameError(e)) throw new IOException(e); throw e; } } @Override public void close() throws IOException { in.close(); } @Override public Timeout timeout() { return timeout; } @Override public String toString() { return "source(" in ")"; } }; }

BufferedSink的创建

跟BuffedSource很相似, 简略描述

sink = Okio.buffer(Okio.sink(rawSocket));

同样主要看Okio.sink(rawSocket)的实现

public static Sink sink(Socket socket) throws IOException { if (socket == null) throw new IllegalArgumentException("socket == null"); AsyncTimeout timeout = timeout(socket); //用socket的outputstream创建sink Sink sink = sink(socket.getOutputStream(), timeout); return timeout.sink(sink); }

sink静态方法的实现

private static Sink sink(final OutputStream out, final Timeout timeout) { if (out == null) throw new IllegalArgumentException("out == null"); if (timeout == null) throw new IllegalArgumentException("timeout == null"); return new Sink() { @Override public void write(Buffer source, long byteCount) throws IOException { checkOffsetAndCount(source.size, 0, byteCount); while (byteCount > 0) { //每次write之前检测timeout timeout.throwIfReached(); Segment head = source.head; int toCopy = (int) Math.min(byteCount, head.limit - head.pos); //最终调用outputstream的write方法 out.write(head.data, head.pos, toCopy); head.pos = toCopy; byteCount -= toCopy; source.size -= toCopy; if (head.pos == head.limit) { source.head = head.pop(); SegmentPool.recycle(head); } } } @Override public void flush() throws IOException { out.flush(); } @Override public void close() throws IOException { out.close(); } @Override public Timeout timeout() { return timeout; } @Override public String toString() { return "sink(" out ")"; } }; }

写在最后:

码字不易看到最后了,那就点个关注呗,只收藏不点关注的都是在耍流氓!

关注并私信我“架构”,免费送一些Java架构资料,先到先得!记得转发哦!

,