Retrofit,OkHttp,Okio Square 安卓平臺(tái)網(wǎng)絡(luò)層三板斧源碼學(xué)習(xí)
基于 okio 1.13.0 版本 okio github 地址
簡(jiǎn)介
Okio 主要是替代 java.io 和 java.nio 那一套復(fù)雜的 api 調(diào)用框架,讓數(shù)據(jù)訪問(wèn)、存儲(chǔ)和處理更加便捷。
Okio 是 OkHttp 的基石,而 OkHttp 是 Retrofit 的基石。
使用方式
參考 OkioTest
……
@Test public void readWriteFile() throws Exception {
File file = temporaryFolder.newFile();
BufferedSink sink = Okio.buffer(Okio.sink(file));
sink.writeUtf8("Hello, java.io file!");
sink.close();
assertTrue(file.exists());
assertEquals(20, file.length());
BufferedSource source = Okio.buffer(Okio.source(file));
assertEquals("Hello, java.io file!", source.readUtf8());
source.close();
}
……
通過(guò) OkioTest 可以大致明白 Okio 主要有 『讀』、『寫(xiě)』兩大類操作。可以操作的對(duì)象為:
1. 文件
2. 文件路徑描述類 Path
3. Socket
4. OutputStream
5. InputStream
Okio 通過(guò) sink(xxx) 去寫(xiě)一個(gè)對(duì)象,通過(guò) source(xxx)去讀一個(gè)對(duì)象。
一覽 Okio 讀寫(xiě)框架
通過(guò) Okio.buffer() 獲得用來(lái)讀寫(xiě)的 BufferedSource、BufferedSink
BufferedSink sink = Okio.buffer(Okio.sink(file));
BufferedSource source = Okio.buffer(Okio.source(file));
進(jìn)一步查看 buffer() 方法
public static BufferedSource buffer(Source source) {
return new RealBufferedSource(source);
}
public static BufferedSink buffer(Sink sink) {
return new RealBufferedSink(sink);
}
RealBufferedSink
看下 RealBufferedSink 類
final class RealBufferedSink implements BufferedSink {
public final Buffer buffer = new Buffer();
public final Sink sink;
boolean closed;
RealBufferedSink(Sink sink) {
if (sink == null) throw new NullPointerException("sink == null");
this.sink = sink;
}
……
}
RealBufferedSink 實(shí)現(xiàn)了 BufferedSink 接口,BufferedSink 實(shí)現(xiàn)了 Sink 接口。
而 Sink 實(shí)現(xiàn)了 Closeable, Flushable 接口。
1. Flushable 接口只定義了一個(gè)方法 flush() ,用來(lái)實(shí)現(xiàn)把數(shù)據(jù)從緩存區(qū)寫(xiě)入到底層輸入流。
2. Closeable 接口定義 close() ,用來(lái)關(guān)閉流。
3. Sink 接口又定義了一個(gè) write(Buffer source, long byteCount) 和 timeout() 用來(lái)寫(xiě)入數(shù)據(jù)和設(shè)置超時(shí)。
4. BufferedSink 接口則定義了一堆 wirteXXX(……) 用來(lái)操作不同類型的數(shù)據(jù)寫(xiě)入。
在看 RealBufferedSink 的成員變量
public final Buffer buffer = new Buffer();
public final Sink sink;
boolean closed;
這里出現(xiàn)了一個(gè) Buffer 對(duì)象,一個(gè)從構(gòu)造函數(shù)里面?zhèn)魅氲?Sink 對(duì)象,以及一個(gè)用來(lái)記錄流是否關(guān)閉的 boolean 標(biāo)志。
RealBufferedSink 的各種 wirteXXX(……)大都如下
@Override public BufferedSink writeXXX(……) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.writeXXX(……);
return emitCompleteSegments();
}
可見(jiàn)寫(xiě)入數(shù)據(jù)的方法,都是有 buffer 對(duì)象實(shí)現(xiàn)。而在 emitXXX() 和 flush() 方法中則調(diào)用了 sink.write(buffer, byteCount) 和 sink.flush()
RealBufferedSource
RealBufferedSource 和 RealBufferedSink 類似
final class RealBufferedSource implements BufferedSource {
public final Buffer buffer = new Buffer();
public final Source source;
boolean closed;
RealBufferedSource(Source source) {
if (source == null) throw new NullPointerException("source == null");
this.source = source;
}
}
RealBufferedSource 實(shí)現(xiàn)了 BufferedSource 接口,BufferedSource 實(shí)現(xiàn)了 Source 接口。
Source 接口同樣也實(shí)現(xiàn)了 Closeable 接口。
1. Source 集成了 Closeable 接口,表示 Source 提供了一個(gè) close 方法關(guān)閉讀取數(shù)據(jù)的流。
2. Source 定義了一個(gè) read(Buffer sink, long byteCount) 用來(lái)讀取數(shù)據(jù),一個(gè) timeout() 方法用來(lái)設(shè)置讀取超時(shí)。
3. BufferedSource 定義了很多 readXXX(……) 用來(lái)讀取數(shù)據(jù)。
RealBufferedSource 中的 readXXX(……) 方法和 RealBufferedSink 中的 writeXXX(……) 類似,都是通過(guò)成員變量 buffer 和 構(gòu)造對(duì)象時(shí)傳入的 Source 對(duì)象配合起來(lái)讀取數(shù)據(jù)。
總結(jié)一下整個(gè)讀寫(xiě)框架的結(jié)構(gòu)如下:
對(duì)所有讀寫(xiě)對(duì)象的統(tǒng)一處理
無(wú)論是 File 、Path、InputStream、OutputStream 、Socket ,在 Okio 框架中只要一個(gè)簡(jiǎn)單的 Okio.sink(……) 方法即可獲得對(duì)應(yīng)的輸入流(RealBufferedSink)和輸出流(RealBufferedSource)
而且 Okio 還給輸入/輸出流的都提供一個(gè)額外參數(shù):Timeout,用來(lái)設(shè)置讀寫(xiě)的超時(shí)設(shè)置。
所有的 sink 方法,都會(huì)調(diào)用到
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) {
timeout.throwIfReached();
Segment head = source.head;
int toCopy = (int) Math.min(byteCount, head.limit - head.pos);
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 + ")";
}
};
}
Okio.sink() 會(huì)創(chuàng)建一個(gè)匿名內(nèi)部類的實(shí)例,實(shí)現(xiàn)了 write 方法,用來(lái)寫(xiě)入數(shù)據(jù)到 OutputStream(File 、Path、Socket 都會(huì)被轉(zhuǎn)成成 OutputStream(),每次寫(xiě)入數(shù)據(jù)的時(shí)候,都會(huì)檢測(cè)是否超時(shí)。(超時(shí)機(jī)制后面后講)
Okio.Source() 類似會(huì)創(chuàng)建一個(gè)實(shí)現(xiàn) Source 接口的匿名內(nèi)部類實(shí)例。實(shí)現(xiàn) read 方法 ,負(fù)責(zé)從 InputStream 中讀取數(shù)據(jù)。
Okio 在讀寫(xiě)數(shù)據(jù)的時(shí)候,里面都會(huì)用用一個(gè) Segment 對(duì)象。Segment 是 Okio 定義的一個(gè)鏈表結(jié)構(gòu)的數(shù)據(jù)片段,每個(gè) Segment 可以最多存放 8K 的字節(jié)。
萬(wàn)能的 Buffer
寫(xiě)數(shù)據(jù)的時(shí)候 Okio 會(huì)先把數(shù)據(jù)寫(xiě)到 buffer 中
BufferedSink sink = Okio.buffer(Okio.sink(file));
sink.writeUtf8("Hello, java.io file!");
sink.close();
Okio.buffer() 返回的是 RealBufferedSink
@Override public BufferedSink writeUtf8(String string) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.writeUtf8(string);
return emitCompleteSegments();
}
查看 writeUtf8
@Override public Buffer writeUtf8(String string) {
return writeUtf8(string, 0, string.length());
}
然后把 String 變成一個(gè) Segment 鏈表
@Override public Buffer writeUtf8(String string, int beginIndex, int endIndex) {
……
// Transcode a UTF-16 Java String to UTF-8 bytes.
for (int i = beginIndex; i < endIndex;) {
int c = string.charAt(i);
if (c < 0x80) {
Segment tail = writableSegment(1);
byte[] data = tail.data;
……
while (i < runLimit) {
c = string.charAt(i);
if (c >= 0x80) break;
data[segmentOffset + i++] = (byte) c; // 0xxxxxxx
}
……
} else if (c < 0x800) {
// Emit a 11-bit character with 2 bytes.
writeByte(c >> 6 | 0xc0); // 110xxxxx
writeByte(c & 0x3f | 0x80); // 10xxxxxx
i++;
} ……
}
return this;
}
通過(guò) writableSegment 是不是要開(kāi)辟新的 Segment 到隊(duì)列尾部
Segment writableSegment(int minimumCapacity) {
if (minimumCapacity < 1 || minimumCapacity > Segment.SIZE) throw new IllegalArgumentException();
if (head == null) {
head = SegmentPool.take(); // Acquire a first segment.
return head.next = head.prev = head;
}
Segment tail = head.prev;
if (tail.limit + minimumCapacity > Segment.SIZE || !tail.owner) {
tail = tail.push(SegmentPool.take()); // Append a new empty segment to fill up.
}
return tail;
}
在看 emitCompleteSegments()
@Override
public BufferedSink emitCompleteSegments() throws IOException {
if (closed) throw new IllegalStateException("closed");
long byteCount = buffer.completeSegmentByteCount();
if (byteCount > 0) sink.write(buffer, byteCount);
return this;
}
buffer.completeSegmentByteCount() 用來(lái)計(jì)算 Segment 的緩存的字節(jié)長(zhǎng)度
public long completeSegmentByteCount() {
long result = size;
if (result == 0) return 0;
// Omit the tail if it's still writable.
Segment tail = head.prev;
if (tail.limit < Segment.SIZE && tail.owner) {
result -= tail.limit - tail.pos;
}
return result;
}
sink.write(buffer, byteCount) 就是之前傳入的集成的 Sink 匿名類。
總結(jié)一下整個(gè)流程
讀數(shù)據(jù)的時(shí)候 Buffer 起到的作用類似,直接貼一下流程圖
Okio 超時(shí)機(jī)制
Okio 可以給他 OutputStream 、InputStream 增加一個(gè)超市設(shè)置。讀寫(xiě)文件時(shí)會(huì)設(shè)置一個(gè)默認(rèn)的 TimeOut ,這個(gè)方法是個(gè)空實(shí)現(xiàn)。
在讀寫(xiě) Socket 的時(shí)候,Okio 給我們展示一個(gè)如何設(shè)置一個(gè)異步的超時(shí)機(jī)制,用來(lái)在 Socket 讀寫(xiě)超時(shí)時(shí)關(guān)閉流。
public static Sink sink(Socket socket) throws IOException {
if (socket == null) throw new IllegalArgumentException("socket == null");
AsyncTimeout timeout = timeout(socket);
Sink sink = sink(socket.getOutputStream(), timeout);
return timeout.sink(sink);
}
先看 timeout(socket)
private static AsyncTimeout timeout(final Socket socket) {
return new AsyncTimeout() {
@Override
protected IOException newTimeoutException(@Nullable IOException cause) {
……
}
@Override
protected void timedOut() {
try {
socket.close();
}……
}
};
}
這里看出會(huì)返回一個(gè) AsyncTimeout 的匿名對(duì)象,主要在 timeOut() 中關(guān)閉 Socket。
sink(socket.getOutputStream(), timeout) 方法在上面已經(jīng)看過(guò)了主要看其中的一句代碼
private static Sink sink(final OutputStream out, final Timeout timeout) {
……
return new Sink() {
@Override
public void write(Buffer source, long byteCount) throws IOException {
……
while (byteCount > 0) {
timeout.throwIfReached();
……
}
}
……
};
}
在看一下 throwIfReached 方法
public void throwIfReached() throws IOException {
if (Thread.interrupted()) {
throw new InterruptedIOException("thread interrupted");
}
if (hasDeadline && deadlineNanoTime - System.nanoTime() <= 0) {
throw new InterruptedIOException("deadline reached");
}
}
如果 hasDeadline 是 true,并且 deadlineNanoTime 大于 System.nanoTime() 來(lái)判斷是否達(dá)超時(shí)。
在看 timeout.sink(sink)
public final Sink sink(final Sink sink) {
return new Sink() {
@Override
public void write(Buffer source, long byteCount) throws IOException {
checkOffsetAndCount(source.size, 0, byteCount);
while (byteCount > 0L) {
// Count how many bytes to write. This loop guarantees we split on a segment boundary.
long toWrite = 0L;
for (Segment s = source.head; toWrite < TIMEOUT_WRITE_SIZE; s = s.next) {
int segmentSize = s.limit - s.pos;
toWrite += segmentSize;
if (toWrite >= byteCount) {
toWrite = byteCount;
break;
}
}
// Emit one write. Only this section is subject to the timeout.
boolean throwOnTimeout = false;
enter();
try {
sink.write(source, toWrite);
byteCount -= toWrite;
throwOnTimeout = true;
} catch (IOException e) {
throw exit(e);
} finally {
exit(throwOnTimeout);
}
}
}
@Override
public void flush() throws IOException {
boolean throwOnTimeout = false;
enter();
try {
sink.flush();
throwOnTimeout = true;
} catch (IOException e) {
throw exit(e);
} finally {
exit(throwOnTimeout);
}
}
@Override
public void close() throws IOException {
boolean throwOnTimeout = false;
enter();
try {
sink.close();
throwOnTimeout = true;
} catch (IOException e) {
throw exit(e);
} finally {
exit(throwOnTimeout);
}
}
@Override
public Timeout timeout() {
return AsyncTimeout.this;
}
@Override
public String toString() {
return "AsyncTimeout.sink(" + sink + ")";
}
};
}
可以看出 timeout.sink(sink) 重新包裝了 Sink 給 Sink 的每個(gè)方法都增加一個(gè) enter() 方法
public final void enter() {
if (inQueue) throw new IllegalStateException("Unbalanced enter/exit");
long timeoutNanos = timeoutNanos();
boolean hasDeadline = hasDeadline();
if (timeoutNanos == 0 && !hasDeadline) {
return; // No timeout and no deadline? Don't bother with the queue.
}
inQueue = true;
scheduleTimeout(this, timeoutNanos, hasDeadline);
}
這里會(huì)發(fā)現(xiàn)如果滿足了條件,會(huì)執(zhí)行 scheduleTimeout 方法。但是默認(rèn)情況下,條件不會(huì)被滿足。
查看一下 SocketTimeoutTest
@Test
public void readWithoutTimeout() throws Exception {
Socket socket = socket(ONE_MB, 0);
BufferedSource source = Okio.buffer(Okio.source(socket));
source.timeout().timeout(5000, TimeUnit.MILLISECONDS);
source.require(ONE_MB);
socket.close();
}
這里可以看到,需要調(diào)用 source.timeout().timeout(5000, TimeUnit.MILLISECONDS)
public Timeout timeout(long timeout, TimeUnit unit) {
if (timeout < 0) throw new IllegalArgumentException("timeout < 0: " + timeout);
if (unit == null) throw new IllegalArgumentException("unit == null");
this.timeoutNanos = unit.toNanos(timeout);
return this;
}
這里可以看到 timeoutNanos 在這里賦值了。所以設(shè)置 timeout(5000, TimeUnit.MILLISECONDS) 后會(huì)出發(fā) scheduleTimeout(this, timeoutNanos, hasDeadline)
private static synchronized void scheduleTimeout(
AsyncTimeout node, long timeoutNanos, boolean hasDeadline) {
// Start the watchdog thread and create the head node when the first timeout is scheduled.
if (head == null) {
head = new AsyncTimeout();
new Watchdog().start();
}
……
// Insert the node in sorted order.
long remainingNanos = node.remainingNanos(now);
for (AsyncTimeout prev = head; true; prev = prev.next) {
if (prev.next == null || remainingNanos < prev.next.remainingNanos(now)) {
node.next = prev.next;
prev.next = node;
if (prev == head) {
AsyncTimeout.class.notify(); // Wake up the watchdog when inserting at the front.
}
break;
}
}
}
這里用到一個(gè)同步鎖、啟動(dòng)一個(gè) Watchdog 線程。并且根據(jù) timeout 的超時(shí)時(shí)間,把 AsyncTimeout 添加到一個(gè)任務(wù)隊(duì)列中。
private static final class Watchdog extends Thread {
Watchdog() {
super("Okio Watchdog");
setDaemon(true);
}
public void run() {
while (true) {
try {
AsyncTimeout timedOut;
synchronized (AsyncTimeout.class) {
timedOut = awaitTimeout();
// Didn't find a node to interrupt. Try again.
if (timedOut == null) continue;
// The queue is completely empty. Let this thread exit and let another watchdog thread
// get created on the next call to scheduleTimeout().
if (timedOut == head) {
head = null;
return;
}
}
// Close the timed out node.
timedOut.timedOut();
} catch (InterruptedException ignored) {
}
}
}
}
Watchdog 線程會(huì)一直同步遍歷任務(wù)隊(duì)列執(zhí)行 awaitTimeout()
static @Nullable
AsyncTimeout awaitTimeout() throws InterruptedException {
// Get the next eligible node.
AsyncTimeout node = head.next;
// The queue is empty. Wait until either something is enqueued or the idle timeout elapses.
if (node == null) {
long startNanos = System.nanoTime();
AsyncTimeout.class.wait(IDLE_TIMEOUT_MILLIS);
return head.next == null && (System.nanoTime() - startNanos) >= IDLE_TIMEOUT_NANOS
? head // The idle timeout elapsed.
: null; // The situation has changed.
}
long waitNanos = node.remainingNanos(System.nanoTime());
// The head of the queue hasn't timed out yet. Await that.
if (waitNanos > 0) {
// Waiting is made complicated by the fact that we work in nanoseconds,
// but the API wants (millis, nanos) in two arguments.
long waitMillis = waitNanos / 1000000L;
waitNanos -= (waitMillis * 1000000L);
AsyncTimeout.class.wait(waitMillis, (int) waitNanos);
return null;
}
// The head of the queue has timed out. Remove it.
head.next = node.next;
node.next = null;
return node;
}
}
這里會(huì)根據(jù)隊(duì)列頭部的 AsyncTimeout 節(jié)點(diǎn),計(jì)算出剩余時(shí)間,然后執(zhí)行 AsyncTimeout.class.wait(waitMillis, (int) waitNanos) 方法阻塞。
注意這個(gè)的 wait(timeout) 會(huì)被 AsyncTimeout.class.notify() 喚醒。
如果任務(wù)隊(duì)列為空會(huì)執(zhí)行 AsyncTimeout.class.wait(IDLE_TIMEOUT_MILLIS) ,等待一分鐘。然后再判斷是否有新的任務(wù)。