转载请注明出处:http://blog.csdn.net/evan_man/article/details/51204469
上一节《OkHttp深入学习(三)——Cache》我们对okhttp中的Cache缓存机制进行了学习,学习了上一节的内容,如果叫我们自己去设计一个缓存机制,那么我们一定会有了自己的思路,想想还有点小激动。这一节我们继续来看看okhttp这个教科书中还有什么值得我们继续挖掘的东西。果不其然,我们发现了okio这个好东西,该类主要负责对java中io的封装,使得java中的io流读写更加方便,甚至还能提高读写效率。okio项目开源地址请戳这里。在正式学习之前,我们先来了解一下它是如何使用的,随后我们再根据涉及到的内容进行深入学习。
okio的数据存储实体
okio的输入输出流
- Sink定义了四个方法write(Buffer source, long byteCount), flush(), close(), and timeout();
- Source定义了三个方法long read(Buffer sink, long byteCount), close(), and timeout();
- BufferedSink定义了writeUtf8、writeString、writeByte、writeShort、 writeInt、writeLong等常用方法;
- BufferedSource定义了readByte()、readShort()、readInt()、readLong()、readUtf8()等常用方法;
okio的实例
private static final ByteString PNG_HEADER = ByteString.decodeHex("89504e470d0a1a0a");
public void decodePng(InputStream in) throws IOException {
BufferedSource pngSource = Okio.buffer(Okio.source(in)); //note1
ByteString header = pngSource.readByteString(PNG_HEADER.size()); //note2
if (!header.equals(PNG_HEADER)) {
throw new IOException("Not a PNG.");
}
while (true) {
Buffer chunk = new Buffer(); //note3
// Each chunk is a length, type, data, and CRC offset.
int length = pngSource.readInt(); //note4
String type = pngSource.readUtf8(4);
pngSource.readFully(chunk, length); //note5
int crc = pngSource.readInt();
decodeChunk(type, chunk);
if (type.equals("IEND")) break;
}
pngSource.close(); //note7
}
private void decodeChunk(String type, Buffer chunk) {
if (type.equals("IHDR")) {
int width = chunk.readInt(); //note6
int height = chunk.readInt();
System.out.printf("%08x: %s %d x %d%n", chunk.size(), type, width, height);
} else {
System.out.printf("%08x: %s%n", chunk.size(), type);
}
}
okio的原理介绍
ByteString
static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; //很明显这个肯定是在进行hex解析的时候被使用到
final byte[] data;
transient int hashCode; //说是String远方亲戚也不假,String也有类似上面的两个域
transient String utf8; //这个域的存在就是实现了对utf-8解码的优化,是一个对utf8解码String的优化
ByteString(byte[] data) {
this.data = data;
}
public static ByteString of(byte... data) {
if (data == null) throw new IllegalArgumentException("data == null");
return new ByteString(data.clone());//note1
}
public static ByteString of(byte[] data, int offset, int byteCount) {
if (data == null) throw new IllegalArgumentException("data == null");
checkOffsetAndCount(data.length, offset, byteCount);//note2
byte[] copy = new byte[byteCount];
System.arraycopy(data, offset, copy, 0, byteCount);//note3
return new ByteString(copy);
}
public String toString() {
if (data.length == 0) {
return "ByteString[size=0]";
}
if (data.length <= 16) {
return String.format("ByteString[size=%s data=%s]", data.length, hex());
}
return String.format("ByteString[size=%s md5=%s]", data.length, md5().hex());
}
public String utf8() {
String result = utf8;
return result != null ? result : (utf8 = new String(data, Util.UTF_8));
}
Segment.class
static final int SIZE = 2048; //一个Segment存储的最大比特数据的数量
final byte[] data; //比特数组的引用
int pos; //pos第一个可以读的位置
int limit; //limit是第一个可以写的位置,所以一个Segment的可读数据数量为pos~limit-1=limit-pos;limit和pos的有效值为0~SIZE-1
boolean shared; //当前存储的data数据是其它对象共享的则为真
boolean owner; //是当前data的所有者
Segment next; //下一个Segment
Segment prev; //前一个Segment
Segment() {
this.data = new byte[SIZE];
this.owner = true; //note1
this.shared = false; //note2
}
Segment(Segment shareFrom) {
this(shareFrom.data, shareFrom.pos, shareFrom.limit);
shareFrom.shared = true; //note3
}
Segment(byte[] data, int pos, int limit) {
this.data = data;
this.pos = pos;
this.limit = limit;
this.owner = false; //note4
this.shared = true; //note5
}
public Segment pop() {
Segment result = next != this ? next : null;
prev.next = next;
next.prev = prev;
next = null;
prev = null;
return result;
}
public Segment push(Segment segment) {
segment.prev = this;
segment.next = next;
next.prev = segment;
next = segment;
return segment;
}
public Segment split(int byteCount) {
if (byteCount <= 0 || byteCount > limit - pos) throw new IllegalArgumentException();
Segment prefix = new Segment(this);
prefix.limit = prefix.pos + byteCount;
pos += byteCount;
prev.push(prefix);
return prefix;
}
public void compact() {
if (prev == this) throw new IllegalStateException();
if (!prev.owner) return; //note1
int byteCount = limit - pos; //note2
int availableByteCount = SIZE - prev.limit + (prev.shared ? 0 : prev.pos); //note3
if (byteCount > availableByteCount) return; //note4
writeTo(prev, byteCount); //note5
pop(); //note6
SegmentPool.recycle(this); //note7
}
public void writeTo(Segment sink, int byteCount) {
if (!sink.owner) throw new IllegalArgumentException(); //note1
if (sink.limit + byteCount > SIZE) { //note2
if (sink.shared) throw new IllegalArgumentException(); //note3
if (sink.limit + byteCount - sink.pos > SIZE) throw new IllegalArgumentException(); //note4
System.arraycopy(sink.data, sink.pos, sink.data, 0, sink.limit - sink.pos); //note5
sink.limit -= sink.pos;
sink.pos = 0;
}
System.arraycopy(data, pos, sink.data, sink.limit, byteCount);//note6
sink.limit += byteCount;
pos += byteCount;
}
SegmentPool.class
static final long MAX_SIZE = 64 * 1024; // 大家是否还记得一个Segment记录的数据最大长度为2048?因此该Segment相当于能存储32个Segment对象。不过为何不是32*2048?
static Segment next; //该SegmentPool存储了一个回收Segment的链表
static long byteCount; //该值记录当前存储的所有Segment总大小,最大值为MAX_SIZE
static Segment take() {
synchronized (SegmentPool.class) {
if (next != null) {
Segment result = next;
next = result.next;
result.next = null;
byteCount -= Segment.SIZE;
return result;
}
}
return new Segment(); // Pool is empty. Don't zero-fill while holding a lock.
}
static void recycle(Segment segment) {
if (segment.next != null || segment.prev != null) throw new IllegalArgumentException();
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;
}
}
Buffer.class
static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; //很明显这个肯定是在进行hex解析的时候被使用到
Segment head; //Buffer存储了一个这样的head结点,这就是Buffer对数据的存储结构。字节数组都是交给Segment进行管理。
long size; //当前存储的数据的大小
public Buffer() {
}
public Buffer copyTo(Buffer out, long offset, long byteCount) {
if (out == null) throw new IllegalArgumentException("out == null");
checkOffsetAndCount(size, offset, byteCount);
if (byteCount == 0) return this;
out.size += byteCount; //note1
Segment s = head;
for (; offset >= (s.limit - s.pos); s = s.next) { //note2
offset -= (s.limit - s.pos);
}
// note3
for (; byteCount > 0; s = s.next) {
Segment copy = new Segment(s);
copy.pos += offset;
copy.limit = Math.min(copy.pos + (int) byteCount, copy.limit);
if (out.head == null) {
out.head = copy.next = copy.prev = copy;
} else {
out.head.prev.push(copy);
}
byteCount -= copy.limit - copy.pos;
offset = 0;
}
return this;
}
public int readInt() {
if (size < 4) throw new IllegalStateException("size < 4: " + size); //note1
Segment segment = head;
int pos = segment.pos;
int limit = segment.limit;
//note2
if (limit - pos < 4) {
return (readByte() & 0xff) << 24
| (readByte() & 0xff) << 16
| (readByte() & 0xff) << 8
| (readByte() & 0xff);
}
//note3
byte[] data = segment.data;
int i = (data[pos++] & 0xff) << 24
| (data[pos++] & 0xff) << 16
| (data[pos++] & 0xff) << 8
| (data[pos++] & 0xff);
size -= 4;
if (pos == limit) { //note4
head = segment.pop();
SegmentPool.recycle(segment);
} else {
segment.pos = pos;
}
return i; //note5
}
public byte readByte() {
if (size == 0) throw new IllegalStateException("size == 0");
Segment segment = head;
int pos = segment.pos;
int limit = segment.limit;
byte[] data = segment.data;
byte b = data[pos++]; //note1
size -= 1;
if (pos == limit) { //note2
head = segment.pop();
SegmentPool.recycle(segment);
} else {
segment.pos = pos;
}
return b; //note3
}
Source source(final InputStream in) { return source(in, new Timeout());}
Source source(File file) throws FileNotFoundException{ return source(new FileInputStream(file)); }
Source source(Path path, OpenOption... options) throws IOException { return source(Files.newInputStream(path, options)); }
Source source(final Socket socket) throws IOException{
AsyncTimeout timeout = timeout(socket);
Source source = source(socket.getInputStream(), timeout);
return timeout.source(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;
timeout.throwIfReached();//note1
Segment tail = sink.writableSegment(1); //note2
int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit); //note3
int bytesRead = in.read(tail.data, tail.limit, maxToCopy); //note4
if (bytesRead == -1) return -1;
tail.limit += bytesRead;//note5
sink.size += bytesRead;
return bytesRead;
}
@Override public void close() throws IOException {
in.close();
}
@Override public Timeout timeout() {
return timeout;
}
@Override public String toString() {
return "source(" + in + ")";
}
};
}
Sink sink(final OutputStream out) { return sink(out, new Timeout()); }
Sink sink(File file) throws FileNotFoundException{ return sink(new FileOutputStream(file)); }
Sink appendingSink(File file) throws FileNotFoundException{ return sink(new FileOutputStream(file, true)); }
Sink sink(Path path, OpenOption... options) throws IOException{ return sink(Files.newOutputStream(path, options)); }
Sink sink(final Socket socket) throws IOException{
AsyncTimeout timeout = timeout(socket);
Sink sink = sink(socket.getOutputStream(), timeout);
return timeout.sink(sink);
}
public static Sink sink(final 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; //note1
int toCopy = (int) Math.min(byteCount, head.limit - head.pos);//note2
out.write(head.data, head.pos, toCopy); //note3
head.pos += toCopy;//note4
byteCount -= toCopy;
source.size -= toCopy;
if (head.pos == head.limit) {//note4
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;
}
@Override public String toString() {
return "sink(" + out + ")";
}
};
}
RealBufferedSource.class
public static BufferedSource buffer(Source source) {
if (source == null) throw new IllegalArgumentException("source == null");
return new RealBufferedSource(source);
}
public RealBufferedSource(Source source) {
this(source, new Buffer()); //note1
}
public RealBufferedSource(Source source, Buffer buffer) {
if (source == null) throw new IllegalArgumentException("source == null");
this.buffer = buffer;
this.source = source;
}
public long read(Buffer sink, long byteCount) throws IOException {
if (sink == null) throw new IllegalArgumentException("sink == null");
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
if (closed) throw new IllegalStateException("closed");
if (buffer.size == 0) { //note1
long read = source.read(buffer, Segment.SIZE);
if (read == -1) return -1;
}
long toRead = Math.min(byteCount, buffer.size); //note2
return buffer.read(sink, toRead); //note3
}
public int readInt() throws IOException {
require(4);
return buffer.readInt();
}
public void require(long byteCount) throws IOException {
if (!request(byteCount)) throw new EOFException(); //没有读到要求的数据宝宝表示不开心,后果很严重
}
public boolean request(long byteCount) throws IOException {
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
if (closed) throw new IllegalStateException("closed");
while (buffer.size < byteCount) {
if (source.read(buffer, Segment.SIZE) == -1) return false; //note1
}
return true;
}
RealBufferedSink.class
public static BufferedSink buffer(Sink sink) {
if (sink == null) throw new IllegalArgumentException("sink == null");
return new RealBufferedSink(sink);
}
public RealBufferedSink(Sink sink) {
this(sink, new Buffer());
}
public RealBufferedSink(Sink sink, Buffer buffer) {
if (sink == null) throw new IllegalArgumentException("sink == null");
this.buffer = buffer;
this.sink = sink;
}
public BufferedSink write(byte[] source) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.write(source);//note1
return emitCompleteSegments();//note2
}
public BufferedSink emitCompleteSegments() throws IOException {
if (closed) throw new IllegalStateException("closed");
long byteCount = buffer.completeSegmentByteCount(); //note1
if (byteCount > 0) sink.write(buffer, byteCount);
return this;
}
public BufferedSink writeInt(int i) throws IOException {
if (closed) throw new IllegalStateException("closed");
buffer.writeInt(i); //note1
return emitCompleteSegments(); //note2
}
public void flush() throws IOException {
if (closed) throw new IllegalStateException("closed");
if (buffer.size > 0) {
sink.write(buffer, buffer.size);
}
sink.flush();
}