上一篇我们着重讲了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操作会抛出中断异常。