上一篇我們著重講了Okio對輸入流的處理,同時也講了Okio的頁式內存管理。本章將著重講一下Okio的輸出操作,由于輸出造作和輸入操作本身就有很多相似點,因此本章將簡單過一下輸出流。
Okio的輸出流是通過Okio.sink方法構造:
public static Sink sink(OutputStream out) {
return sink(out, new Timeout());
}
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) {//當head讀取完畢,回收掉內存碎片
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;
}
};
}
1.sink方法一樣是構造了一個Sink接口的匿名類,其中最重要的就是 write(Buffer source, long byteCount) 。
2.按照我們之前對Okio的認識,實際上,Okio只是在java的stream平臺上構建的一層裝飾庫,因此,不論是flush操作還是write操作,最終都是要調用到stream.flush或者stream.write方法。
3.Sink接口的目的,就是將存在于內存Buffer對象中的數據,write到輸出流中。
那么復制多少呢?
int toCopy = (int) Math.min(byteCount, head.limit - head.pos);
“toCopy”變量用于記錄所需要復制的數據長度,其中byteCount代表調用時候傳入的數據長度,head.limit代表head數據片中有的數據,head.pos指向還未讀取的數據位置。由于Okio的讀取是分塊讀取的(存儲也是分塊的),因此,在塊中所能讀取的數據長度不能超過limit-pos。就這樣,sink就完成了從Buffer到Output流的數據傳遞。
實際上,我們在講輸入和輸出的時候,我們一直忽略了一個參數,那就是超時參數:Timeout對象。我們來看下Okio是如何實現的Timeout機制:
// code Sink.write
while (byteCount > 0) {
timeout.throwIfReached();//超時檢測
....
}
//code Source.read
while (byteCount > 0) {
timeout.throwIfReached();
....
}
我們看到,對于匿名類的Source和Sink對象,實際上都采用輪詢的方式來檢測超時,并且調用timeout的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記錄的結束時間,sink的write操作或者source的read操作會拋出中斷異常。