本篇目录
一、前言
okio是大名鼎鼎的square公司开发出来的,其是okhttp的底层io操作库,既然已经有java原生的io库为什么还要自己费尽开发一套呢?java原生的io操作存在很多问题,比如读写阻塞,内存管理并不高效,体系臃肿,api调用不精简,以上我个人认为okio改进最大的地方是内存管理方面,比如我们拷贝数据java原生io数据转移大体过程如下:
而okio中过程如下:
少了一个中间数据拷贝的过程,这样效率会提升很多,并且okio中数据缓存的处理更是精心设计的,我觉得这部分才是其精华所在:okio将数据(Buffer)采用双向循环链表的方式组织在一起,并且链表每个结点数据存储在一个个数组(Segment)中,结构如下:
这样的存储结构有很多好处,拷贝数据我们可以直接移动指针而不像原生io那样需要一个个字节拷贝过去,这样会大大提高数据转移的效率。
再来简要看一下API的使用简洁性
向file中写入数据,原生io实现如下:
public static void writeTest(File file) {
try {
FileOutputStream fos = new FileOutputStream(file);
OutputStream os = new BufferedOutputStream(fos);
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF("write string by utf-8.\n");
dos.writeInt(1234);
dos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
用okio实现:
public static void writeTest(File file) {
try {
Okio.buffer(Okio.sink(file))
.writeUtf8("write string by utf-8.\n")
.writeInt(1234).close();
} catch (Exception e) {
e.printStackTrace();
}
}
以上demo很好的体现了okio api的简洁性。
通过以上比较你应该能感受到okio的强大之处,但是也要明白一点okio也是完全基于原生InputStream与OutputStream来进行封装的,并没有完全舍弃原生io,可以理解为对原生io的封装扩展,重点优化了缓存部分,至于缓存部分后续分析完源码你会有更深入的理解。
另外okio提供数据访问的超时机制,访问资源可以控制时间。
okio的源码比较简短,建议有时间好好阅读一下。
二、顶级接口Source与Sink
Source与Sink是Okio中的输入流接口和输出流接口,对应原生IO的InputStream和OutputStream。
先看下Source源码:
public interface Source extends Closeable {
long read(Buffer sink, long byteCount) throws IOException;
Timeout timeout();
@Override void close() throws IOException;
}
很简单就定义了几个方法,读数据到sink中以及关闭资源的方法,至于timeout方法暂时先不用管,后面提超时机制的时候会分析。
Sink源码:
public interface Sink extends Closeable, Flushable {
void write(Buffer source, long byteCount) throws IOException;
@Override void flush() throws IOException;
Timeout timeout();
@Override void close() throws IOException;
}
同样比较简单,没什么好说的,自己看一下就可以了。
三、BufferedSource与BufferedSink
BufferedSource与BufferedSink同样是两个接口类,分别继承Source与Sink接口,BufferedSource与BufferedSink是具有缓存功能的接口,各自维护了一个buffer,同时提供了很多实用的api调用接口,平时我们使用也主要是调用这两个类中定义的方法。
BufferedSink类:
public interface BufferedSink extends Sink {
/** Returns this sink's internal buffer. */
Buffer buffer();
BufferedSink write(ByteString byteString) throws IOException;
BufferedSink write(byte[] source) throws IOException;
BufferedSink write(byte[] source, int offset, int byteCount) throws IOException;
long writeAll(Source source) throws IOException;
BufferedSink write(Source source, long byteCount) throws IOException;
BufferedSink writeUtf8(String string) throws IOException;
BufferedSink writeUtf8(String string, int beginIndex, int endIndex) throws IOException;
/** Encodes {@code codePoint} in UTF-8 and writes it to this sink. */
BufferedSink writeUtf8CodePoint(int codePoint) throws IOException;
/** Encodes {@code string} in {@code charset} and writes it to this sink. */
BufferedSink writeString(String string, Charset charset) throws IOException;
BufferedSink writeString(String string, int beginIndex, int endIndex, Charset charset)
throws IOException;
/** Writes a byte to this sink. */
BufferedSink writeByte(int b) throws IOException;
BufferedSink writeShort(int s) throws IOException;
BufferedSink writeShortLe(int s) throws IOException;
BufferedSink writeInt(int i) throws IOException;
BufferedSink writeIntLe(int i) throws IOException;
BufferedSink writeLong(long v) throws IOException;
BufferedSink writeLongLe(long v) throws IOException;
BufferedSink writeDecimalLong(long v) throws IOException;
BufferedSink writeHexadecimalUnsignedLong(long v) throws IOException;
@Override void flush() throws IOException;
BufferedSink emit() throws IOException;
BufferedSink emitCompleteSegments() throws IOException;
/** Returns an output stream that writes to this sink. */
OutputStream outputStream();
}
就是定义了一些写方便的方法,其中emit()与flush()方法刚接触同学可能有些生疏,去看下源码中注释就明白了,其余都比较简单了,不熟悉可以看下注释,老外写代码挺注重注释的~
BufferedSource类源码这里只看一部分了,与BufferedSink对应:
public interface BufferedSource extends Source {
/** Returns this source's internal buffer. */
Buffer buffer();
/**
* Returns when the buffer contains at least {@code byteCount} bytes. Throws an
* {@link java.io.EOFException} if the source is exhausted before the required bytes can be read.
*/
void require(long byteCount) throws IOException;
/**
* Returns true when the buffer contains at least {@code byteCount} bytes, expanding it as
* necessary. Returns false if the source is exhausted before the requested bytes can be read.
*/
boolean request(long byteCount) throws IOException;
/** Removes a byte from this source and returns it. */
byte readByte() throws IOException;
short readShort() throws IOException;
short readShortLe() throws IOException;
long readLong() throws IOException;
/** Removes all bytes bytes from this and returns them as a byte string. */
ByteString readByteString() throws IOException;
/** Removes {@code byteCount} bytes from this and returns them as a byte array. */
byte[] readByteArray(long byteCount) throws IOException;
int read(byte[] sink) throws IOException;
void readFully(byte[] sink) throws IOException;
int read(byte[] sink, int offset, int byteCount) throws IOException;
long readAll(Sink sink) throws IOException;
String readUtf8() throws IOException;
String readUtf8Line() throws IOException;
/** Returns an input stream that reads from this source. */
InputStream inputStream();
}
这里只是列出了部分定义的方法,大体看一下就可以了,就是各种读的方法。
四、 RealBufferedSink 和 RealBufferedSource
上面提到的都是接口类,具体的实现类分别是RealBufferedSink和 RealBufferedSource ,其实这两个类也不算具体实现类,只是Buffer类的代理类,具体功能都在Buffer类里面实现的。
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;
}
@Override public Buffer buffer() {
return buffer;
}
@Override public void write(Buffer source, long byteCount)
throws IOException {
if (closed) throw new IllegalStateException("closed");
//调用buffer的write方法
buffer.write(source, byt