上一篇讲过,okio只有两个概念,source和sink。source对应InputStream,即负责将数据读出,是一个输出方(所以只有source.read方法)。sink对应outputStream,负责获取数据写入,是一个写入方(所以只有sink.write方法)。而对于,source.read(sink)这个方法,实际就是,将source中的数据读出,写入到sink当中。
所以其实source和sink只有数据的流向不一致。所以,这里只介绍了RealBufferedSource,实际是跟RealBufferedSink是一致的。
介绍RealBufferedSource,首先先看下okio的基础用法。
public void readFileUseOkIo() {
try {
BufferedSource source = Okio.buffer(Okio.source(new File(fileName)));
String content;
while ((content = source.readUtf8Line()) != null) {
System.out.println("readFileUseOkIo content:"+content);
}
source.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
注意,一般对应okio,只有两个常用的api,okio.source和okio.buffer方法。
这一行的实际含义是:
创建new File,内部创建new FileInputStream,作为source对应的inputStream。
再调用buffer,将source传入RealBufferedSource。RealBufferedSource同时维护一个Buffer,和一个Source。
Okio.source
source方法接收一个InputStream。并返回一个新创建的Source,重写了read(Buffer,long)方法。
这个方法在哪里会被调用到呢?后面会讲到。先记住source方法实际是重写了read(Buffer,long)方法。
// Okio.Source
private static Source source(final InputStream in, final Timeout timeout) {
if (in == null) {
throw new IllegalArgumentException("in == null");
} else if (timeout == null) {
throw new IllegalArgumentException("timeout == null");
} else {
return new Source() {
// 这里实际上,是从source中的buffer转移数据到sink当中,如果source中没有数据了,
// 就从in中读取数据到source中的buffer
public long read(Buffer sink, long byteCount) throws IOException {
if (byteCount < 0L) {
throw new IllegalArgumentException("byteCount < 0: " + byteCount);
} else if (byteCount == 0L) {
return 0L;
} else {
try {
timeout.throwIfReached();
// 从sink中获取一个可写的segment
Segment tail = sink.writableSegment(1);
int maxToCopy = (int)Math.min(byteCount, (long)(8192 - tail.limit));
// 从in中获取tail中可容纳的空间个字节到tail中
int bytesRead = in.read(tail.data, tail.limit, maxToCopy);
if (bytesRead == -1) {
return -1L;
} else {
tail.limit += bytesRead;
sink.size += (long)bytesRead;
return (long)bytesRead;
}
} catch (AssertionError var7) {
if (Okio.isAndroidGetsocknameError(var7)) {
throw new IOException(var7);
} else {
throw var7;
}
}
}
}
public void close() throws IOException {
in.close();
}
public Timeout timeout() {
return timeout;
}
public String toString() {
return "source(" + in + ")";
}
};
}
}
Okio.buffer
// Okio.buffer方法
public static BufferedSource buffer(Source source) {
return new RealBufferedSource(source);
}
Okio.buffer方法,只简单返回了RealBufferSource方法。
RealBufferedSource实际上是一个Source类型,内部维护了一个Buffer和一个Source。这个Source参数,实际就是刚才okio.source方法新创建的new Source,这个source重写了read(Buffer,long)方法。
// RealBufferedSource.read
// 两个Buffer之间转移,比如Source.read(Sink,byteCount)的时候,会调用这个方法
public long read(Buffer sink, long byteCount) throws IOException {
if (sink == null) {
throw new IllegalArgumentException("sink == null");
} else if (byteCount < 0L) {
throw new IllegalArgumentException("byteCount < 0: " + byteCount);
} else if (this.closed) {
throw new IllegalStateException("closed");
} else {
long read;
if (this.buffer.size == 0L) {// 从这里看出,实际就是先从source读Segment.Size个字节的数据到buffer
// 注意这里,这里实际是okio.source方法那里重写的read方法
// 即这里,如果buffer内数据为空,就会调用stream.read方法,将数据读到buffer,即RealBufferedSource也是一个缓冲区。
read = this.source.read(this.buffer, 8192L);
if (read == -1L) {
return -1L;
}
}
read = Math.min(byteCount, this.buffer.size);
// 然后再从buffer读取read个字节的数据到sink。一次最多只读取一个Segment的数据
return this.buffer.read(sink, read);
}
}
RealBufferedSource方法也重写了read(Buffer,long)。这个方法一般用于buffer间的数据交互,比如source的buffer数据,转移到sink的buffer中。
从上面的注释可以看出:
RealBufferSource中持有Source(Stream)和Buffer,Buffer相当于Source的缓冲区,Sink直接跟Buffer进行数据交互。当Buffer中数据被读取完了,Buffer会从Source中读数据。
也即:
总结
okio对外暴露的接口:okio.source和okio.buffer。
其中okio.source,构建了stream到buffer的数据流向。
而okio.buffer,创建了RealBufferedSource。RealBufferedSource维护一个buffer。当需要将数据传给sink的时候,优先从buffer中读取数据到sink。如果buffer为空,就会调用okio.source的方法,从stream中读取数据到buffer。