【安卓开发系列 -- APP 开源框架】IO 框架 OKIO
【1】OKIO的整体框架
【1.1】OKIO的整体框架图示
【1.2】OKIO的优势
CPU和内存消耗低,OKIO采用了segment机制进行内存共享和复用,尽可能少的申请内存,从而降低了GC的频率(过于频繁的GC会给应用程序带来性能问题);
使用方便,OKIO提供了ByteString来处理不变的byte序列,并在内存上做了优化,不管是从byte[]到String或是从String到byte[],操作便利,同时提供了如hex字符,base64等工具;提供了Buffer处理可变byte序列,可以根据使用情况自动增长,在使用过程中也不用去关心position等位置的处理;
N合一(OKIO精简了输入输出流的类个数),对于Java的原生IO,InputStream/OutputStream,若需要读取数据(如读取一个整数,一个布尔,或是一个浮点),需要用DataInputStream来包装;若作为缓存来使用,则需要使用BufferedOutputStream包装;OKIO提供的BufferedSink/BufferedSource具备以上功能,从而不需要再串联上一系列的装饰类;
提供了一系列的方便工具,如GZip的透明处理,对数据计算md5、sha1等的支持,使得数据校验方便;
【2】OKIO使用方法示例
【2.1】OKIO示例代码
File inFile = new File(Environment.getExternalStorageDirectory() + "/" + "input.txt");
File outFile = new File(Environment.getExternalStorageDirectory() + "/" + "output.txt");
String name = "xiaoming";
int age = 11;
try {
// 向文件写入数据
Okio.buffer(Okio.sink(inFile)).writeUtf8(name).writeInt(age).close();
// 拷贝文件
Okio.buffer(Okio.sink(inFile)).writeAll(Okio.buffer(Okio.source(outFile)));
// 从文件读取数据
System.out.println(Okio.buffer(Okio.source(outFile)).readString(StandardCharsets.UTF_8));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
【2.2】OKIO读写流程图示
【3】OKIO源码分析
【3.1】Source/Sink接口
类继承结构图
Source接口源码
/**
* Supplies a stream of bytes. Use this interface to read data from wherever
* it's located: from the network, storage, or a buffer in memory.
*
* 提供字节流,使用此接口可以从任何位置读取数据(网络、存储器或内存中的缓冲区)
*/
public interface Source extends Closeable {
/**
* Removes at least 1, and up to {@code byteCount} bytes from this and appends
* them to {@code sink}. Returns the number of bytes read, or -1 if this
* source is exhausted.
*
* 移除1至byteCount个字节并将这些字节追加到接收器(sink)
* 返回读取的字节数,若source读取完毕则返回-1;
*/
long read(Buffer sink, long byteCount) throws IOException;
/** Returns the timeout for this source. */
// 返回source的timeout值
Timeout timeout();
/**
* Closes this source and releases the resources held by this source. It is an
* error to read a closed source. It is safe to close a source more than once.
*
* 关闭此源并释放此源持有的资源
* 读取关闭的源将产生错误
* 源可以关闭多次
*/
@Override void close() throws IOException;
}
/**
* A source that keeps a buffer internally so that callers can do small reads without a performance
* penalty. It also allows clients to read ahead, buffering as much as necessary before consuming
* input.
*
* 一种在内部保留缓冲区的源,从而调用者可以在不影响性能的情况下进行小的读取
* 还允许客户端进行预读,在使用输入之前尽可能多地进行缓冲
*/
public interface BufferedSource extends Source, ReadableByteChannel {
/** Returns this source's internal buffer. */
// 返回该source的内部buffer
Buffer buffer();
/**
* Returns true if there are no more bytes in this source. This will block until there are bytes
* to read or the source is definitely exhausted.
*
* 如果此源中没有更多的字节则返回true
* 该方法将阻塞直到有字节可读取或源绝对耗尽
*/
boolean exhausted() throws IOException;
/**
* 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.
*
* 当缓冲区至少包含byteCount个字节时返回
* 若在读取所需字节之前源已用尽则抛出java.io.EOFException异常
*/
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.
*
* 当缓冲区至少包含byteCount个字节时返回true,并根据需要对其进行扩展
* 如果在读取请求的字节之前源已用尽则返回false
*/
boolean request(long byteCount) throws IOException;
/** Removes a byte from this source and returns it. */
// 从源移除一个字节并返回该字节
byte readByte() throws IOException;
/**
* Removes two bytes from this source and returns a big-endian short. <pre>{@code
* 从源移除两个字节并返回一个大端的short变量
*/
short readShort() throws IOException;
/**
* Removes two bytes from this source and returns a little-endian short. <pre>{@code
* 从源移除两个字节并返回一个小端的short变量
*/
short readShortLe() throws IOException;
int readInt() throws IOException;
int readIntLe() throws IOException;
long readLong() throws IOException;
long readLongLe() throws IOException;
/**
* Reads a long from this source in signed decimal form (i.e., as a string in base 10 with
* optional leading '-')
* 以带符号的十进制的形式从源中读取long变量
*/
long readDecimalLong() throws IOException;
/**
* Reads a long form this source in hexadecimal form (i.e., as a string in base 16). This will
* iterate until a non-hexadecimal character is found. <pre>{@code
* 以十六进制形式从源中读取long变量
* 该方法将迭代直到找到非十六进制字符
*/
long readHexadecimalUnsignedLong() throws IOException;
/**
* Reads and discards {@code byteCount} bytes from this source. Throws an
* {@link java.io.EOFException} if the source is exhausted before the
* requested bytes can be skipped.
* 从该源读取并丢弃byteCount字节
* 如果在可以跳过请求的字节之前源已用尽则抛出java.io.EOFException
*/
void skip(long byteCount) 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 string. */
// 从源中删除byteCount个字节字节并作为字节字符串返回
ByteString readByteString(long byteCount) throws IOException;
/**
* Finds the first string in {@code options} that is a prefix of this buffer, consumes it from
* this buffer, and returns its index. If no byte string in {@code options} is a prefix of this
* buffer this returns -1 and no bytes are consumed.
* 在options中查找作为此缓冲区前缀的第一个字符串,从该缓冲区消费它并返回其索引
*/
int select(Options options) throws IOException;
/** Removes all bytes from this and returns them as a byte array. */
// 从源中删除所有字节并作为字节数组返回
byte[] readByteArray() throws IOException;
/** Removes {@code byteCount} bytes from this and returns them as a byte array. */
// 从中删除byteCount个字节并作为字节数组返回
byte[] readByteArray(long byteCount) throws IOException;
/**
* Removes up to {@code sink.length} bytes from this and copies them into {@code sink}. Returns
* the number of bytes read, or -1 if this source is exhausted.
* 删除最多sink.length个字节并复制到sink
*/
int read(byte[] sink) throws IOException;
/**
* Removes exactly {@code sink.length} bytes from this and copies them into {@code sink}. Throws
* an {@link java.io.EOFException} if the requested number of bytes cannot be read.
* 完全删除sink.length个字节并将其复制到sink
* 如果无法读取需要的字节数则抛出java.io.EOFException异常
*/
void readFully(byte[] sink) throws IOException;
/**
* Removes up to {@code byteCount} bytes from this and copies them into {@code sink} at {@code
* offset}. Returns the number of bytes read, or -1 if this source is exhausted.
* 从中删除最多byteCount个字节并复制到sink的offset处
*/
int read(byte[] sink, int offset, int byteCount) throws IOException;
/**
* Removes exactly {@code byteCount} bytes from this and appends them to {@code sink}. Throws an
* {@link java.io.EOFException} if the requested number of bytes cannot be read.
* 删除byteCount个字节并附加到sink
* 如果无法读取需要的字节数则抛出java.io.EOFException异常
*/
void readFully(Buffer sink, long byteCount) throws IOException;
/**
* Removes all bytes from this and appends them to {@code sink}. Returns the total number of bytes
* written to {@code sink} which will be 0 if this is exhausted.
* 从中删除所有字节并附加到sink
*/
long readAll(Sink sink) throws IOException;
/**
* Removes all bytes from this, decodes them as UTF-8, and returns the string. Returns the empty
* string if this source is empty.
* 从中删除所有字节解码为UTF-8并返回字符串;如果此源为空则返回空字符串;
*/
String readUtf8() throws IOException;
/**
* Removes {@code byteCount} bytes from this, decodes them as UTF-8, and returns the string.
* 从中删除byteCount个字节解码为UTF-8并返回字符串
*/
String readUtf8(long byteCount) throws IOException;
/**
* Removes and returns characters up to but not including the next line break. A line break is
* either {@code "\n"} or {@code "\r\n"}; these characters are not included in the result.
* 删除并返回下一个换行符但不包括下一个换行符
*/
@Nullable String readUtf8Line() throws IOException;
/**
* Removes and returns characters up to but not including the next line break. A line break is
* either {@code "\n"} or {@code "\r\n"}; these characters are not included in the result.
* 删除并返回下一个换行符但不包括下一个换行符
*/
String readUtf8LineStrict() throws IOException;
String readUtf8LineStrict(long limit) throws IOException;
/**
* Removes and returns a single UTF-8 code point, reading between 1 and 4 bytes as necessary.
* 移除并传回单一的UTF-8码位,必要时读取1至4个位元组
*/
int readUtf8CodePoint() throws IOException;
/** Removes all bytes from this, decodes them as {@code charset}, and returns the string. */
// 从中删除所有字节解码为charset并返回字符串
String readString(Charset charset) throws IOException;
String readString(long byteCount, Charset charset) throws IOException;
long indexOf(byte b) throws IOException;
/**
* Returns the index of the first {@code b} in the buffer at or after {@code fromIndex}. This
* expands the buffer as necessary until {@code b} is found. This reads an unbounded number of
* bytes into the buffer. Returns -1 if the stream is exhausted before the requested byte is
* found.
* 返回缓冲区中位于fromindex处或之后的第一个b的索引
*/
long indexOf(byte b, long fromIndex) throws IOException;
long indexOf(byte b, long fromIndex, long toIndex) throws IOException;
long indexOf(ByteString bytes) throws IOException;
/**
* Returns the index of the first match for {@code bytes} in the buffer at or after {@code
* fromIndex}. This expands the buffer as necessary until {@code bytes} is found. This reads an
* unbounded number of bytes into the buffer. Returns -1 if the stream is exhausted before the
* requested bytes are found.
* 返回缓冲区中bytes在codefromindex处或之后的第一个匹配项的索引
*/
long indexOf(ByteString bytes, long fromIndex) throws IOException;
long indexOfElement(ByteString targetBytes) throws IOException;
/**
* Returns the first index in this buffer that is at or after {@code fromIndex} and that contains
* any of the bytes in {@code targetBytes}. This expands the buffer as necessary until a target
* byte is found. This reads an unbounded number of bytes into the buffer. Returns -1 if the
* stream is exhausted before the requested byte is found.
* 返回此缓冲区中位于{@codefromindex}处或之后且包含{@code targetBytes}中任何字节的第一个索引
*/
long indexOfElement(ByteString targetBytes, long fromIndex) throws IOException;
/**
* Returns true if the bytes at {@code offset} in this source equal {@code bytes}. This expands
* the buffer as necessary until a byte does not match, all bytes are matched, or if the stream
* is exhausted before enough bytes could determine a match.
* 如果此源中offset处的字节等于bytes则返回true
*/
boolean rangeEquals(long offset, ByteString bytes) throws IOException;
/**
* Returns true if {@code byteCount} bytes at {@code offset} in this source equal {@code bytes}
* at {@code bytesOffset}. This expands the buffer as necessary until a byte does not match, all
* bytes are matched, or if the stream is exhausted before enough bytes could determine a match.
*/
boolean rangeEquals(long offset, ByteString bytes, int bytesOffset, int byteCount)
throws IOException;
/** Returns an input stream that reads from this source. */
// 返回从source中读取的输入流
InputStream inputStream();
}
Sink接口源码
/**
* Receives a stream of bytes. Use this interface to write data wherever it's
* needed: to the network, storage, or a buffer in memory. Sinks may be layered
* to transform received data, such as to compress, encrypt, throttle, or add
* protocol framing.
*
* 接收字节流
* 使用此接口可以在需要的地方写入数据(网络、存储器或内存中的缓冲区)
* 接收器可以分层以转换接收的数据,例如压缩、加密、调节或添加协议帧
*/
public interface Sink extends Closeable, Flushable {
/** Removes {@code byteCount} bytes from {@code source} and appends them to this. */
// 从source移除byteCount个字节并追加到sink中
void write(Buffer source, long byteCount) throws IOException;
/** Pushes all buffered bytes to their final destination. */
// 将所有缓冲字节推送到最终目的地
@Override void flush() throws IOException;
/** Returns the timeout for this sink. */
Timeout timeout();
/**
* Pushes all buffered bytes to their final destination and releases the
* resources held by this sink. It is an error to write a closed sink. It is
* safe to close a sink more than once.
*
* 将所有缓冲字节推送到最终目的地并释放此接收器保留的资源
* 写已经关闭的sink将产生错误
* 可以多次关闭一个sink
*/
@Override void close() throws IOException;
}
/**
* A sink that keeps a buffer internally so that callers can do small writes
* without a performance penalty.
*
* 一种在内部保留缓冲区的接收器,从而调用者可以在不影响性能的情况下进行小的写入
*/
public interface BufferedSink extends Sink, WritableByteChannel {
/** Returns this sink's internal buffer. */
// 返回该sink的内部buffer
Buffer buffer();
// 写方法
BufferedSink write(ByteString byteString) throws IOException;
/**
* 写入一个完整的字节数组
*/
BufferedSink write(byte[] source) throws IOException;
/**
* 从字节数组源source的offset开始写入byteCount个字节
*/
BufferedSink write(byte[] source, int offset, int byteCount) throws IOException;
/**
* 移除source中的所有字节并追加到sink中
* 返回读取的字节数
*/
long writeAll(Source source) throws IOException;
// 移除source中的byteCount个字节并追加到sink中
BufferedSink write(Source source, long byteCount) throws IOException;
/**
* 对string进行UTF-8编码并追加到sink中
*/
BufferedSink writeUtf8(String string) throws IOException;
/**
* 对string字符串beginIndex到endIndex的字符进行UTF-8编码并追加到sink中
*/
BufferedSink writeUtf8(String string, int beginIndex, int endIndex) throws IOException;
/** Encodes {@code codePoint} in UTF-8 and writes it to this sink. */
// 对int型数据进行UTF-8编码并追加到sink中
BufferedSink writeUtf8CodePoint(int codePoint) throws IOException;
/** Encodes {@code string} in {@code charset} and writes it to this sink. */
// 对字符串进行指定类型的编码并追加到sink中
BufferedSink writeString(String string, Charset charset) throws IOException;
// 对字符串的beginIndex到endIndex进行指定类型的编码并追加到sink中
BufferedSink writeString(String string, int beginIndex, int endIndex, Charset charset)
throws IOException;
/** Writes a byte to this sink. */
// 写入一个字节到sink中
BufferedSink writeByte(int b) throws IOException;
// 使用两个字节向sink中写入大端short变量
BufferedSink writeShort(int s) throws IOException;
// 使用两个字节向sink中写入小端short变量
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;
/**
* Writes a long to this sink in signed decimal form (i.e., as a string in base 10). <pre>{@code
* 以带符号的十进制的形式向sink写入long变量
*/
BufferedSink writeDecimalLong(long v) throws IOException;
/**
* Writes a long to this sink in hexadecimal form
* 以十六进制形式向sink写入long变量
*/
BufferedSink writeHexadecimalUnsignedLong(long v) throws IOException;
/**
* Writes all buffered data to the underlying sink, if one exists. Then that sink is recursively
* flushed which pushes data as far as possible towards its ultimate destination. Typically that
* destination is a network socket or file.
*
* 将所有缓冲数据写入基础接收器
* 然后递归地刷新该接收器,从而将数据尽可能地推向最终目的地
* 通常该目的地址是网络套接字或文件
*/
@Override void flush() throws IOException;
/**
* Writes all buffered data to the underlying sink, if one exists. Like {@link #flush}, but
* weaker. Call this before this buffered sink goes out of scope so that its data can reach its
* destination. <pre>{@code
*
* 将所有缓冲数据写入基础接收器
* 类似于flush方法但比较弱
* 在该缓冲接收器超出作用域之前调用它,以便其数据可以到达目的地
*/
BufferedSink emit() throws IOException;
/**
* Writes complete segments to the underlying sink, if one exists. Like {@link #flush}, but
* weaker. Use this to limit the memory held in the buffer to a single segment. Typically
* application code will not need to call this: it is only necessary when application code writes
* directly to this {@linkplain #buffer() sink's buffer}. <pre>{@code
*
* 将完整段写入基础接收器
* 类似于flush方法但比较弱
* 使用该方法可将缓冲区中的内存限制为单个段
* 通常,应用程序代码不需要调用这个函数:
* 只有当应用程序代码直接写入该sink的buffer时才需要调用该方法
*/
BufferedSink emitCompleteSegments() throws IOException;
/** Returns an output stream that writes to this sink. */
// 返回写入此接收器的输出流
OutputStream outputStream();
}
【3.2】Buffer类
类继承结构图示
Buffer类原理图示
Buffer源码分析
public final class Okio {
// Returns a new source that buffers reads from {@code source}
// 返回新的source,该源可以缓存从其中读取的数据
public static BufferedSource buffer(Source source) {
// RealBufferedSource类内部包含Buffer实例buffer
// RealBufferedSource类中的read相关方法都转发给该Buffer实例buffer对应的read方法处理
return new RealBufferedSource(source);
}
/** Returns a source that reads from {@code file}. */
// 根据传入的File实例构建一个Source实例
public static Source source(File file) throws FileNotFoundException {
if (file == null) throw new IllegalArgumentException("file == null");
return source(new FileInputStream(file));
}
/** Returns a source that reads from {@code in}. */
// 根据InputStream实例构建一个Source实例
public static Source source(InputStream in) {
return source(in, new Timeout());
}
// 创建一个Source实例(带有超时)并实现接口中的方法
private static Source source(final InputStream in, final Timeout timeout) {
if (in == null) throw new IllegalArgumentException("in == null");
if (timeout == null) throw new IllegalArgumentException("timeout == null");
return new Source() {
@Override public long read(Buffer sink, long byteCount) throws IOException {
// 检查入参的有效性
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
if (byteCount == 0) return 0;
try {
// 超时检查
timeout.throwIfReached();
// 获取一个可以写的Segment
/**
* Returns a tail segment that we can write at least minimumCapacity bytes to, creating it if necessary.
* 返回tail指向的至少可以写入minimumCapacity容量字节Segment并且在必要时创建该Segment
*/
Segment tail = sink.writableSegment(1);
int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit);
int bytesRead = in.read(tail.data, tail.limit, maxToCopy);
if (bytesRead == -1) return -1;
tail.limit += bytesRead;
sink.size += bytesRead;
return bytesRead;
} catch (AssertionError e) {
if (isAndroidGetsocknameError(e)) throw new IOException(e);
throw e;
}
}
// 关闭输出流
@Override public void close() throws IOException {
in.close();
}
// 返回Timeout实例
@Override public Timeout timeout() {
return timeout;
}
// 对象的toString方法覆写
@Override public String toString() {
return "source(" + in + ")";
}
};
}
}
public final class Okio {
// Returns a new sink that buffers writes to {@code sink}
// 返回新的sink,该sink可以缓存待写入其中的数据
public static BufferedSink buffer(Sink sink) {
// RealBufferedSource类内部包含Buffer实例buffer
// RealBufferedSource类中的write相关方法都转发给该Buffer实例buffer对应的write方法处理
return new RealBufferedSink(sink);
}
/** Returns a sink that writes to {@code file}. */
// 根据传入的File实例构建一个Sink实例
public static Sink sink(File file) throws FileNotFoundException {
if (file == null) throw new IllegalArgumentException("file == null");
return sink(new FileOutputStream(file));
}
/** Returns a sink that writes to {@code out}. */
// 根据OutputStream实例构建一个Sink实例
public static Sink sink(OutputStream out) {
return sink(out, new Timeout());
}
// 创建一个Sink实例(带有超时)并实现接口中的方法
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");
// 创建一个匿名Sink对象
return new Sink() {
// 覆写write方法
@Override public void write(Buffer source, long byteCount) throws IOException {
// 检查Buffer的有效性
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;
// 当一个Segment读取完毕则将该Segment放入SegmentPool中
if (head.pos == head.limit) {
// 源缓冲source双向Segment列表中弹出一个Segment
source.head = head.pop();
// SegmentPool回收弹出的Segment
SegmentPool.recycle(head);
}
}
}
// 刷新输出流
@Override public void flush() throws IOException {
out.flush();
}
// 关闭输出流
@Override public void close() throws IOException {
out.close();
}
// 返回Timeout实例
@Override public Timeout timeout() {
return timeout;
}
// 对象的toString方法覆写
@Override public String toString() {
return "sink(" + out + ")";
}
};
}
}
【3.3】Segment/SegmentPool
// A segment of a buffer
// 缓冲区中的一个分片
final class Segment {
/** The size of all segments in bytes. */
// 分片中的总字节数
static final int SIZE = 8192;
/** Segments will be shared when doing so avoids {@code arraycopy()} of this many bytes. */
//
static final int SHARE_MINIMUM = 1024;
// 存储数据
final byte[] data;
/** The next byte of application data byte to read in this segment. */
// 下一次读取开始的位置
int pos;
/** The first byte of available data ready to be written to. */
// 写入的开始位置
int limit;
/** True if other segments or byte strings use the same byte array. */
// 若其他段或字节字符串使用相同的字节数组则取值为true
boolean shared;
/** True if this segment owns the byte array and can append to it, extending {@code limit}. */
// 如果这个段拥有字节数组并且可以附加到该字节数组则取值为true
// 标记data是否仅当前Segment独有
boolean owner;
/** Next segment in a linked or circularly-linked list. */
// 循环双向链表的下一个节点
Segment next;
/** Previous segment in a circularly-linked list. */
// 循环双向链表的前一个节点
Segment prev;
// Segment的构造方法
// SegmentPool的take方法中调用
Segment() {
this.data = new byte[SIZE];
this.owner = true;
this.shared = false;
}
// Segment的构造方法
// Segment类的sharedCopy/unsharedCopy以及SegmentedByteString类的write方法中调用
Segment(byte[] data, int pos, int limit, boolean shared, boolean owner) {
this.data = data;
this.pos = pos;
this.limit = limit;
this.shared = shared;
this.owner = owner;
}
/**
* Returns a new segment that shares the underlying byte array with this. Adjusting pos and limit
* are safe but writes are forbidden. This also marks the current segment as shared, which
* prevents it from being pooled.
*
* 返回与该段共享字节数组的新段
* 调整位置和限制是安全的但禁止写入
* 将当前段标记为共享段并阻止该段被池化
*/
Segment sharedCopy() {
shared = true;
return new Segment(data, pos, limit, true, false);
}
/** Returns a new segment that its own private copy of the underlying byte array. */
// 返回一个新的段,该段拥有自己独有的底层字节数组
Segment unsharedCopy() {
return new Segment(data.clone(), pos, limit, false, true);
}
/**
* Removes this segment of a circularly-linked list and returns its successor.
* Returns null if the list is now empty.
* 将当前段从循环链接列表中删除并返回其后续项
* 如果当前列表为空则返回null
*/
public @Nullable Segment pop() {
Segment result = next != this ? next : null;
prev.next = next;
next.prev = prev;
next = null;
prev = null;
return result;
}
/**
* Appends {@code segment} after this segment in the circularly-linked list.
* Returns the pushed segment.
* 将入参segment追加到循环链接列表中的当前段之后
* 返回加入的段
*/
public Segment push(Segment segment) {
segment.prev = this;
segment.next = next;
next.prev = segment;
next = segment;
return segment;
}
/**
* Splits this head of a circularly-linked list into two segments. The first
* segment contains the data in {@code [pos..pos+byteCount)}. The second
* segment contains the data in {@code [pos+byteCount..limit)}. This can be
* useful when moving partial segments from one buffer to another.
* 将循环链表的头拆分为两段
* 第一段包含{@code[pos..pos+byteCount)}中的数据
* 第二段包含{@code [pos+byteCount..limit)}中的数据
* 在将部分段从一个缓冲区移动到另一个缓冲区时非常有用
*/
public Segment split(int byteCount) {
if (byteCount <= 0 || byteCount > limit - pos) throw new IllegalArgumentException();
Segment prefix;
// We have two competing performance goals:
// - Avoid copying data. We accomplish this by sharing segments.
// - Avoid short shared segments. These are bad for performance because they are readonly and
// may lead to long chains of short segments.
// To balance these goals we only share segments when the copy will be large.
//
// 性能处理
// - 通过共享段避免拷贝数据
// - 避免短的共享段,过短的段对性能不利,因为这些段是只读的并且这些短段会导致过长的链表
// 在副本比较大的时候共享段以平衡上述的目标
if (byteCount >= SHARE_MINIMUM) {
prefix = sharedCopy();
} else {
prefix = SegmentPool.take();
System.arraycopy(data, pos, prefix.data, 0, byteCount);
}
prefix.limit = prefix.pos + byteCount;
pos += byteCount;
prev.push(prefix);
return prefix;
}
/**
* Call this when the tail and its predecessor may both be less than half
* full. This will copy data so that segments can be recycled.
* 将当前Segment结点和prev前驱结点合并成一个Segment并统一合并到prev
* 然后当前Segment结点从双向链表移除并添加到SegmentPool复用;
* 合并的前提是,2个Segment的字节总和不超过8K,合并后可能会移动pos、limit
*/
public void compact() {
if (prev == this) throw new IllegalStateException();
if (!prev.owner) return; // Cannot compact: prev isn't writable.
int byteCount = limit - pos;
int availableByteCount = SIZE - prev.limit + (prev.shared ? 0 : prev.pos);
if (byteCount > availableByteCount) return; // Cannot compact: not enough writable space.
writeTo(prev, byteCount);
// 将当前段从循环链接列表中删除并返回其后续项
pop();
// 回收当前Segment到SegmentPool中
SegmentPool.recycle(this);
}
/** Moves {@code byteCount} bytes from this segment to {@code sink}. */
// 从当前节点移动byteCount个字节到{@code sink}中
public void writeTo(Segment sink, int byteCount) {
if (!sink.owner) throw new IllegalArgumentException();
// sink中可写的空间不足
if (sink.limit + byteCount > SIZE) {
// We can't fit byteCount bytes at the sink's current position. Shift sink first.
// 判断sink是否只读
if (sink.shared) throw new IllegalArgumentException();
// 判断sink中的剩余空间是否满足写入的字节数
if (sink.limit + byteCount - sink.pos > SIZE) throw new IllegalArgumentException();
// sink中的数据前移
System.arraycopy(sink.data, sink.pos, sink.data, 0, sink.limit - sink.pos);
sink.limit -= sink.pos;
sink.pos = 0;
}
// 将当前Segment中的数据拷贝到sink中
System.arraycopy(data, pos, sink.data, sink.limit, byteCount);
sink.limit += byteCount;
pos += byteCount;
}
}
/**
* A collection of unused segments, necessary to avoid GC churn and zero-fill.
* This pool is a thread-safe static singleton.
* 未使用段的集合,注意避免GC混乱和零填充
* 此池是线程安全的静态单例
*/
final class SegmentPool {
/** The maximum number of bytes to pool. */
// 池的最大字节数
static final long MAX_SIZE = 64 * 1024; // 64 KiB.
/** Singly-linked list of segments. */
// Segment的单向链列表
static @Nullable Segment next;
/** Total bytes in this pool. */
// 此池中的总字节数
static long byteCount;
// SegmentPool的构造方法
private SegmentPool() {
}
// 从池中获取一个Segment对象
static Segment take() {
synchronized (SegmentPool.class) {
if (next != null) {
Segment result = next;
next = result.next;
result.next = null;
byteCount -= Segment.SIZE;
return result;
}
}
// 返回一个Segment实例,不要在持有锁的时候将Segment置零
return new Segment(); // Pool is empty. Don't zero-fill while holding a lock.
}
// 将Segment状态初始化并回收到池中
static void recycle(Segment segment) {
if (segment.next != null || segment.prev != null) throw new IllegalArgumentException();
// shared标记的Segment不能被回收
if (segment.shared) return; // This segment cannot be recycled.
synchronized (SegmentPool.class) {
if (byteCount + Segment.SIZE > MAX_SIZE) return; // Pool is full.
byteCount += Segment.SIZE;
segment.next = next;
segment.pos = segment.limit = 0;
next = segment;
}
}
}
【3.4】Timeout机制
【3.4.1】Timeout类
// Timeout 类
public class Timeout {
/**
* An empty timeout that neither tracks nor detects timeouts. Use this when
* timeouts aren't necessary, such as in implementations whose operations
* do not block.
* 空的Timeout实例,该实例既不跟踪也不检测超时
* 通常在不需要超时时使用,比如非阻塞的操作
*/
public static final Timeout NONE = new Timeout() {
@Override public Timeout timeout(long timeout, TimeUnit unit) {
return this;
}
@Override public Timeout deadlineNanoTime(long deadlineNanoTime) {
return this;
}
@Override public void throwIfReached() throws IOException {
}
};
/**
* True if {@code deadlineNanoTime} is defined. There is no equivalent to null
* or 0 for {@link System#nanoTime}.
* 如果定义了{@code deadlineNanoTime}则为True
*/
private boolean hasDeadline;
// 截止时间点
private long deadlineNanoTime;
// timeout等待的时间
private long timeoutNanos;
public Timeout() {
}
/**
* Wait at most {@code timeout} time before aborting an operation. Using a
* per-operation timeout means that as long as forward progress is being made,
* no sequence of operations will fail.
* 在中止操作之前最多等待{@code timeout}
* 对每个操作设置超时意味着,只要正在进行前向处理,任何操作序列都不会失败
*/
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;
}
/** Returns the timeout in nanoseconds, or {@code 0} for no timeout. */
// 返回timeout时间大小
public long timeoutNanos() {
return timeoutNanos;
}
/** Returns true if a deadline is enabled. */
// 返回是否使能了deadline
public boolean hasDeadline() {
return hasDeadline;
}
/**
* Returns the {@linkplain System#nanoTime() nano time} when the deadline will
* be reached.
* 当到达deadline时间点时返回deadline的值
*/
public long deadlineNanoTime() {
if (!hasDeadline) throw new IllegalStateException("No deadline");
return deadlineNanoTime;
}
/**
* Sets the {@linkplain System#nanoTime() nano time} when the deadline will be
* reached. All operations must complete before this time. Use a deadline to
* set a maximum bound on the time spent on a sequence of operations.
* 设置deadline
* 在deadline之前所有的操作必须完成
* 使用deadline设置操作序列所花费时间的最大限制
*/
public Timeout deadlineNanoTime(long deadlineNanoTime) {
this.hasDeadline = true;
this.deadlineNanoTime = deadlineNanoTime;
return this;
}
/** Set a deadline of now plus {@code duration} time. */
// 设置deadline,按时间单位转换为纳秒
public final Timeout deadline(long duration, TimeUnit unit) {
if (duration <= 0) throw new IllegalArgumentException("duration <= 0: " + duration);
if (unit == null) throw new IllegalArgumentException("unit == null");
return deadlineNanoTime(System.nanoTime() + unit.toNanos(duration));
}
/** Clears the timeout. Operating system timeouts may still apply. */
// 清除timeout,从而操作可以继续执行
public Timeout clearTimeout() {
this.timeoutNanos = 0;
return this;
}
/** Clears the deadline. */
// 关闭deadline
public Timeout clearDeadline() {
this.hasDeadline = false;
return this;
}
/**
* Throws an {@link InterruptedIOException} if the deadline has been reached or if the current
* thread has been interrupted. This method doesn't detect timeouts; that should be implemented to
* asynchronously abort an in-progress operation.
* 当deadline到达并且当前线程中断后抛出InterruptedIOException异常
* 此方法不检测超时,应实现超时以异步中止正在进行的操作
*/
public void throwIfReached() throws IOException {
if (Thread.interrupted()) {
throw new InterruptedIOException("thread interrupted");
}
if (hasDeadline && deadlineNanoTime - System.nanoTime() <= 0) {
throw new InterruptedIOException("deadline reached");
}
}
/**
* Waits on {@code monitor} until it is notified. Throws {@link InterruptedIOException} if either
* the thread is interrupted or if this timeout elapses before {@code monitor} is notified. The
* caller must be synchronized on {@code monitor}.
* 在{@code monitor}等待直到接收到通知
* 在线程中断或在{@code monitor}通知之前超时抛出InterruptedIOException异常
* 调用者必须在{@code monitor}上同步
*/
public final void waitUntilNotified(Object monitor) throws InterruptedIOException {
try {
boolean hasDeadline = hasDeadline();
long timeoutNanos = timeoutNanos();
if (!hasDeadline && timeoutNanos == 0L) {
monitor.wait(); // There is no timeout: wait forever.
return;
}
// Compute how long we'll wait.
// 计算等待的时间
long waitNanos;
long start = System.nanoTime();
if (hasDeadline && timeoutNanos != 0) {
long deadlineNanos = deadlineNanoTime() - start;
waitNanos = Math.min(timeoutNanos, deadlineNanos);
} else if (hasDeadline) {
waitNanos = deadlineNanoTime() - start;
} else {
waitNanos = timeoutNanos;
}
// Attempt to wait that long. This will break out early if the monitor is notified.
long elapsedNanos = 0L;
if (waitNanos > 0L) {
long waitMillis = waitNanos / 1000000L;
// 线程在监视器上等待一定的时间
monitor.wait(waitMillis, (int) (waitNanos - waitMillis * 1000000L));
elapsedNanos = System.nanoTime() - start;
}
// Throw if the timeout elapsed before the monitor was notified.
// 若timeout在监视器的通知到达之前超过则抛出InterruptedIOException异常
if (elapsedNanos >= waitNanos) {
throw new InterruptedIOException("timeout");
}
} catch (InterruptedException e) {
throw new InterruptedIOException("interrupted");
}
}
}
【3.4.2】AsyncTimeout类
// This timeout uses a background thread to take action exactly when the timeout occurs
// 此超时使用后台线程在超时发生时执行操作
public class AsyncTimeout extends Timeout {
/**
* Don't write more than 64 KiB of data at a time, give or take a segment. Otherwise slow
* connections may suffer timeouts even when they're making (slow) progress. Without this, writing
* a single 1 MiB buffer may never succeed on a sufficiently slow connection.
* 一次写的数据不要超过64kb
* 否则慢连接可能会出现超时,即使正在进行(缓慢)处理
* 如果没有这一点则在一个足够慢的连接上写一个1MB缓冲区可能永远不会成功
*/
private static final int TIMEOUT_WRITE_SIZE = 64 * 1024;
/** Duration for the watchdog thread to be idle before it shuts itself down. */
// 看门狗线程在关闭自身之前处于空闲状态的持续时间
private static final long IDLE_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60);
private static final long IDLE_TIMEOUT_NANOS = TimeUnit.MILLISECONDS.toNanos(IDLE_TIMEOUT_MILLIS);
/**
* The watchdog thread processes a linked list of pending timeouts, sorted in the order to be
* triggered. This class synchronizes on AsyncTimeout.class. This lock guards the queue.
* 看门狗线程处理挂起超时的单向链表,timeout项按触发的顺序排序
*/
static @Nullable AsyncTimeout head;
/** True if this node is currently in the queue. */
// 若当前节点在队列中则值为true
private boolean inQueue;
/** The next node in the linked list. */
// 指向单向链表中的下一个节点
private @Nullable AsyncTimeout next;
/** If scheduled, this is the time that the watchdog should time this out. */
// 超时时间
private long timeoutAt;
// 把当前AsyncTimeout对象加入节点并加入链表
public final void enter() {
// 检查参数
if (inQueue) throw new IllegalStateException("Unbalanced enter/exit");
long timeoutNanos = timeoutNanos();
boolean hasDeadline = hasDeadline();
if (timeoutNanos == 0 && !hasDeadline) {
// 在没有deadline和timeout的情况下直接返回
return; // No timeout and no deadline? Don't bother with the queue.
}
inQueue = true;
// 节点入队并开启watchdog线程监视timeout链表
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.
// 若timeout链表为空则新建timeout链表并开始Watchdog线程
if (head == null) {
head = new AsyncTimeout();
new Watchdog().start();
}
long now = System.nanoTime();
// 确定当前节点的timeout时间点
if (timeoutNanos != 0 && hasDeadline) {
// Compute the earliest event; either timeout or deadline. Because nanoTime can wrap around,
// Math.min() is undefined for absolute values, but meaningful for relative ones.
node.timeoutAt = now + Math.min(timeoutNanos, node.deadlineNanoTime() - now);
} else if (timeoutNanos != 0) {
node.timeoutAt = now + timeoutNanos;
} else if (hasDeadline) {
node.timeoutAt = node.deadlineNanoTime();
} else {
throw new AssertionError();
}
// Insert the node in sorted order.
// 计算到达timeout时间点剩余的时间
long remainingNanos = node.remainingNanos(now);
// 遍历timeout链表按照到达timeout的时间点的剩余时间排序
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) {
// 插入节点之后唤醒watchdog线程
AsyncTimeout.class.notify(); // Wake up the watchdog when inserting at the front.
}
break;
}
}
}
/** Returns true if the timeout occurred. */
// 从链表中移除节点,若timeout发生则返回true
public final boolean exit() {
// 节点没有加入timeout链表则直接返回
if (!inQueue) return false;
inQueue = false;
return cancelScheduledTimeout(this);
}
/** Returns true if the timeout occurred. */
// 从链表中移除节点
private static synchronized boolean cancelScheduledTimeout(AsyncTimeout node) {
// Remove the node from the linked list.
// 从链表中移除指定的节点
for (AsyncTimeout prev = head; prev != null; prev = prev.next) {
if (prev.next == node) {
prev.next = node.next;
node.next = null;
return false;
}
}
// The node wasn't found in the linked list: it must have timed out!
// 若节点不在链表中则表明发生了timeout
return true;
}
/**
* 返回到达timeout时间点剩余的时间,该值可以为负,此时timeout立即发生
*/
private long remainingNanos(long now) {
return timeoutAt - now;
}
/**
* 由watchdog线程在timeout发生时调用,处理timeout逻辑
*/
protected void timedOut() {
}
...
/**
* Throws an IOException if {@code throwOnTimeout} is {@code true} and a timeout occurred.
* 如果{@code throwOnTimeout}为{@code true}并且发生超时则抛出IOException
*/
final void exit(boolean throwOnTimeout) throws IOException {
boolean timedOut = exit();
if (timedOut && throwOnTimeout) throw newTimeoutException(null);
}
/**
* Returns either {@code cause} or an IOException that's caused by {@code cause} if a timeout occurred.
* 如果发生超时,则返回{@code cause}或由{@code cause}引起的IOException
*/
final IOException exit(IOException cause) throws IOException {
if (!exit()) return cause;
return newTimeoutException(cause);
}
/**
* Returns an {@link IOException} to represent a timeout.
* 返回一个IOException异常表示timeout
*/
protected IOException newTimeoutException(@Nullable IOException cause) {
InterruptedIOException e = new InterruptedIOException("timeout");
if (cause != null) {
e.initCause(cause);
}
return e;
}
// Watchdog守护线程
private static final class Watchdog extends Thread {
Watchdog() {
super("Okio Watchdog");
setDaemon(true);
}
public void run() {
while (true) {
try {
AsyncTimeout timedOut;
synchronized (AsyncTimeout.class) {
// awaitTimeout方法
// 移除并返回位于链表顶部的节点,如有必要则等待其超时
// 如果在启动时列表的头部没有节点并且在等待{@code IDLE_TIMEOUT_NANOS}之后仍然没有节点则返回{@link}head}
// 如果在等待时插入了新节点则返回null
// 否则,将返回正在等待且已删除的节点
timedOut = awaitTimeout();
// Didn't find a node to interrupt. Try again.
// 返回null则表明链表中插入了新节点
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().
// 返回head表明链表为空则退出当前Watchdog守护线程
// 另一个Watchdog守护线程会在下一次调用scheduleTimeout()创建并启动
if (timedOut == head) {
head = null;
return;
}
}
// Close the timed out node.
// 处理发生timeout的节点的timedOut()方法以实现特定的业务逻辑
timedOut.timedOut();
} catch (InterruptedException ignored) {
}
}
}
}
/**
* Removes and returns the node at the head of the list, waiting for it to time out if necessary.
* 移除并返回位于链表顶部的节点,如有必要则等待其超时
* 如果在启动时列表的头部没有节点并且在等待{@code IDLE_TIMEOUT_NANOS}之后仍然没有节点则返回{@link}head}
* 如果在等待时插入了新节点则返回null
* 否则,将返回正在等待且已删除的节点
*/
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.
// 若链表为空则等到新节点入队或timeout到达
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());
// 链表的首节点没有timeout则等待
// 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.
// 链表的首节点到达了timeout则首节点出队
head.next = node.next;
node.next = null;
return node;
}
}
【3.4.3】具有Timeout机制的Sink与Source
public class AsyncTimeout extends Timeout {
...
/**
* Returns a new sink that delegates to {@code sink}, using this to implement timeouts. This works
* best if {@link #timedOut} is overridden to interrupt {@code sink}'s current operation.
* 返回具有超时功能的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 + ")";
}
};
}
/**
* Returns a new source that delegates to {@code source}, using this to implement timeouts. This
* works best if {@link #timedOut} is overridden to interrupt {@code sink}'s current operation.
* 返回具有超时功能的Source实例
*/
public final Source source(final Source source) {
return new Source() {
@Override public long read(Buffer sink, long byteCount) throws IOException {
boolean throwOnTimeout = false;
enter();
try {
long result = source.read(sink, byteCount);
throwOnTimeout = true;
return result;
} catch (IOException e) {
throw exit(e);
} finally {
exit(throwOnTimeout);
}
}
@Override public void close() throws IOException {
boolean throwOnTimeout = false;
try {
source.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.source(" + source + ")";
}
};
}
...
}
【3.5】生产者与消费者模型(Pipe)
public final class Pipe {
// Pipe的最大容量
final long maxBufferSize;
// Buffer实例用于转发write/read方法
final Buffer buffer = new Buffer();
// 标记Sink是否关闭
boolean sinkClosed;
// 标记Source是否关闭
boolean sourceClosed;
// PipeSink实例,PipeSink为内部类
private final Sink sink = new PipeSink();
// PipeSource实例,PipeSource为内部类
private final Source source = new PipeSource();
// Pipe类的构造方法
public Pipe(long maxBufferSize) {
if (maxBufferSize < 1L) {
throw new IllegalArgumentException("maxBufferSize < 1: " + maxBufferSize);
}
this.maxBufferSize = maxBufferSize;
}
// 返回PipeSource实例
public Source source() {
return source;
}
// 返回PipeSink实例
public Sink sink() {
return sink;
}
// PipeSink类实现了Sink接口并覆写了其中的方法
final class PipeSink implements Sink {
// 内部包含了Timeout实例
final Timeout timeout = new Timeout();
@Override public void write(Buffer source, long byteCount) throws IOException {
synchronized (buffer) {
if (sinkClosed) throw new IllegalStateException("closed");
while (byteCount > 0) {
if (sourceClosed) throw new IOException("source is closed");
long bufferSpaceAvailable = maxBufferSize - buffer.size();
if (bufferSpaceAvailable == 0) {
// buffer中没有剩余空间等待消费者消费
timeout.waitUntilNotified(buffer); // Wait until the source drains the buffer.
continue;
}
long bytesToWrite = Math.min(bufferSpaceAvailable, byteCount);
// 使用Buffer实例写入数据
buffer.write(source, bytesToWrite);
byteCount -= bytesToWrite;
// 通知source有新的数据了
buffer.notifyAll(); // Notify the source that it can resume reading.
}
}
}
@Override public void flush() throws IOException {
synchronized (buffer) {
if (sinkClosed) throw new IllegalStateException("closed");
if (sourceClosed && buffer.size() > 0) throw new IOException("source is closed");
}
}
@Override public void close() throws IOException {
synchronized (buffer) {
if (sinkClosed) return;
if (sourceClosed && buffer.size() > 0) throw new IOException("source is closed");
sinkClosed = true;
// 通知source,此时没有新加入的数据了
buffer.notifyAll(); // Notify the source that no more bytes are coming.
}
}
@Override public Timeout timeout() {
return timeout;
}
}
// PipeSource类实现了Source接口并覆写了其中的方法
final class PipeSource implements Source {
final Timeout timeout = new Timeout();
@Override public long read(Buffer sink, long byteCount) throws IOException {
synchronized (buffer) {
if (sourceClosed) throw new IllegalStateException("closed");
while (buffer.size() == 0) {
if (sinkClosed) return -1L;
// Pipe中没有数据则等待生产者写入
timeout.waitUntilNotified(buffer); // Wait until the sink fills the buffer.
}
long result = buffer.read(sink, byteCount);
// 通知sink可以写入数据
buffer.notifyAll(); // Notify the sink that it can resume writing.
return result;
}
}
@Override public void close() throws IOException {
synchronized (buffer) {
sourceClosed = true;
// 通知sink,此时不再读取数据
buffer.notifyAll(); // Notify the sink that no more bytes are desired.
}
}
@Override public Timeout timeout() {
return timeout;
}
}
}
参考致谢
本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。
【2】拆轮子系列:拆 Okio