Okio的傳送門
了解Okio之前先了解一個(gè)裝飾者模式(就是java io的思路)
- 接下來簡單模擬一個(gè)io操作
- 定義一個(gè)讀取數(shù)據(jù)的接口鸟廓,返回byte[ ],BytesReader
- 定義一個(gè)它的實(shí)現(xiàn)類时捌,用來讀取byte[]挫鸽,BytesReaderImpl
- 定義一個(gè)讀取String的接口粗卜,用來讀取StringReader
- 對(duì)BytesReaderImpl進(jìn)行裝飾,,讓裝飾類支持讀取String贯底,StringReaderImpl
- 看看具體的代碼實(shí)現(xiàn)吧 _( 比較簡單)
ByteReader && ByteReaderImpl
public interface BytesReader {
byte[] readBytes(); //定義一個(gè)讀取byte[]的數(shù)組
}
public class BytesReaderImpl implements BytesReader {
@Override
public byte[] readBytes() {
String str = "僅僅就是用來測(cè)試的字符串^_^...";
return str.getBytes();
}
}
StringReader && StringReaderImpl
public interface StringReader extends BytesReader{
String readString();
}
public class StringReaderImpl implements StringReader{
private BytesReader bytesReader;
public StringReaderImpl(BytesReader bytesReader) {
this.bytesReader = bytesReader;
}
@Override
public String readString() {
byte[] bytes = bytesReader.readBytes();
return new String(bytes);
}
@Override
public byte[] readBytes() {
return bytesReader.readBytes();
}
}
現(xiàn)在來看看okio的基本用法
public class Test {
public static void main(String[] args){
/* BytesReader bytesReader = new BytesReaderImpl();
StringReader stringReader = new StringReaderImpl(bytesReader);
System.out.println("readBytes : "+bytesReader.readBytes().length);
System.out.println("readString : "+stringReader.readString());*/
File file = new File("D://demo.txt");
File fileOut = new File("D://demo1.txt");
BufferedSink sink = null;
BufferedSource source = null;
try {
sink = Okio.buffer(Okio.sink(fileOut));
source = Okio.buffer(Okio.source(file));
byte[] buffer = new byte[12];
int temp = 0;
while((temp = source.read(buffer)) != -1){
System.out.println("temp : "+temp);
sink.write(buffer,0,temp);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(sink != null){
sink.close();
}
if(source != null){
source.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- okio里面有一個(gè),Source接口定義了讀取數(shù)據(jù)的接口撒强,類似InputStream
- okio里面還有一個(gè)禽捆,Sink接口定義了寫數(shù)據(jù)的接口,類似OutputStream
- Source和Sink有兩個(gè)實(shí)現(xiàn)類飘哨,BufferedSource 和 BufferedSink胚想,這兩個(gè)實(shí)現(xiàn)類定義了很多方法,包括文件的讀寫芽隆,字符串的讀寫浊服,流的讀寫 等
- BufferedSource 和 BufferedSink有兩個(gè)之類,RealBufferedSource和RealBufferedSink**胚吁,實(shí)際用的時(shí)候其實(shí)是用的這兩個(gè)實(shí)現(xiàn)類
- Okio這個(gè)類提供了很多靜態(tài)方法牙躺,簡化上面這些類的創(chuàng)建操作
下面開始分析源代碼
-
首先有一點(diǎn)是明白的,就是Source和Sink這兩個(gè)類定義了頂層接口腕扶,一個(gè)用來讀數(shù)據(jù)孽拷,一個(gè)用來寫數(shù)據(jù)。
Source && Sinkpublic interface Source extends Closeable { long read(Buffer sink, long byteCount) throws IOException; Timeout timeout(); @Override void close() throws IOException; } public interface Sink extends Closeable, Flushable { void write(Buffer source, long byteCount) throws IOException; @Override void flush() throws IOException; /** Returns the timeout for this sink. */ Timeout timeout(); @Override void close() throws IOException; }
-
Source和Sink分別有一個(gè)實(shí)現(xiàn)接口BufferedSource(接口)和BufferedSink(接口),這兩個(gè)接口就定義了更加偏向應(yīng)用的常用接口半抱,可以看到不管是讀還是寫都支持常見的類型脓恕,基本類型膜宋,String,而且Source接口和Sink接口接受的參數(shù)都是Buffer
public interface BufferedSink extends Sink { Buffer buffer(); BufferedSink write(ByteString byteString) throws IOException; BufferedSink write(byte[] source, int offset, int byteCount) throws IOException; BufferedSink writeUtf8(String string) throws IOException; BufferedSink writeShort(int s) throws IOException; ..... } public interface BufferedSource extends Source { byte readByte() throws IOException; short readShort() throws IOException; short readShortLe() throws IOException; String readUtf8() throws IOException; ..... }
- 可以看到不管是Source還是Sink接受的參數(shù)都是Buffer [okio.Buffer]炼幔,所以說Buffer在okio的讀寫種起著媒介(比較重要)的作用秋茫,Buffer的代碼有點(diǎn)多(1600+行)就提出一些比較重要的來說
public final class Buffer implements BufferedSource, BufferedSink, Cloneable {
Segment head;
long size;
@Override public OutputStream outputStream() {
return new OutputStream() {
@Override public void write(int b) {
writeByte((byte) b);
}
@Override public void write(byte[] data, int offset, int byteCount) {
Buffer.this.write(data, offset, byteCount);
}
@Override public void flush(){}
@Override public void close() {}
@Override public String toString() {
return Buffer.this + ".outputStream()";
}
};
}
...
@Override public InputStream inputStream() {
return new InputStream() {
@Override public int read() {
if (size > 0) return readByte() & 0xff;
return -1;
}
@Override public int read(byte[] sink, int offset, int byteCount) {
return Buffer.this.read(sink, offset, byteCount);
}
@Override public int available() {
return (int) Math.min(size, Integer.MAX_VALUE);
}
@Override public void close() {
}
@Override public String toString() {
return Buffer.this + ".inputStream()";
}
};
}
....
}
- 先簡單的說哈后面在回過來說,這里Buffer其實(shí)既有讀的功能也有寫的功能乃秀,但是我們的程序里面其實(shí)是可以像調(diào)用java io的api一樣肛著,因?yàn)槔锩姘b了一個(gè)OutputStream和InputStream ,這里還引入了一個(gè)新的對(duì)象环形,**Segment**
-
有了大概的了解策泣,就來看看繼承圖
繼續(xù)啊
- 我們構(gòu)造BufferedSource是使用的是Okio.buffer(Okio.sink(fileOut));
- 首先來看Okio.sink(fileOut),其實(shí)內(nèi)部就一句,return sink(new FileOutputStream(file));抬吟,構(gòu)造了一個(gè)FileOutputStream并調(diào)用了萨咕,sink(OutputStream out)方法,最終調(diào)用了sink(OutputStream out, Timeout timeout) 這個(gè)方法,其實(shí)就是構(gòu)造了一個(gè)Sink接口對(duì)象并返回。
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) { 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 + ")"; } }; }
上面的Sink返回了以后傳遞給了Okio.buffer方法火本,這個(gè)方法里面實(shí)際就是實(shí)例化了一個(gè)RealBufferedSink對(duì)象并返回危队。代碼就不貼了,說哈RealBufferedSink大概做了些什么钙畔,首先是RealBufferedSink里面包含了一個(gè)Buffer(可讀可寫)對(duì)象茫陆,在調(diào)用RealBufferedSink的時(shí)候,實(shí)際上就是調(diào)用的Buffer對(duì)象的write方法擎析。
BufferedSource和BufferedSink的處理是類似的這里就不啰嗦了簿盅。
小結(jié)前面提到的流程
- 在構(gòu)造BufferedSource的時(shí)候會(huì)傳遞一個(gè)Source到Okio.buffer方法里面,而這個(gè)Source是一個(gè)匿名內(nèi)部類來實(shí)例化的揍魂,并且里面使用FileInputStream去讀取數(shù)據(jù)桨醋,然后吧數(shù)據(jù)保存到傳入的Buffer參數(shù)里面,而這個(gè)Buffer是支持讀寫的现斋。所以BufferedSource讀取Buffer里面的數(shù)據(jù)喜最,Buffer獲取從FileInputStream里面的數(shù)據(jù)。從這里就可以看出來庄蹋,Okio效率高就是這個(gè)Buffer在起作用瞬内,前面大概說了哈Buffer,它里面還有一個(gè)重要的對(duì)象還沒有說Segment
繼續(xù)哈
- Segment對(duì)象限书,Segment的源碼不是很多虫蝶,實(shí)現(xiàn)的實(shí)現(xiàn)其實(shí)就是一個(gè)雙向鏈表。里面定義了一個(gè)byte[]和前一個(gè)節(jié)點(diǎn)的引用以及后一個(gè)節(jié)點(diǎn)的引用
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;
int pos;
int limit;
boolean shared;
boolean owner;
Segment next;
Segment prev;
Segment() {
this.data = new byte[SIZE];
this.owner = true;
this.shared = false;
}
...
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 void writeTo(Segment sink, int byteCount) {
if (!sink.owner) throw new IllegalArgumentException();
if (sink.limit + byteCount > SIZE) {
// We can't fit byteCount bytes at the sink's current position. Shift sink first.
if (sink.shared) throw new IllegalArgumentException();
if (sink.limit + byteCount - sink.pos > SIZE) throw new IllegalArgumentException();
System.arraycopy(sink.data, sink.pos, sink.data, 0, sink.limit - sink.pos);
sink.limit -= sink.pos;
sink.pos = 0;
}
System.arraycopy(data, pos, sink.data, sink.limit, byteCount);
sink.limit += byteCount;
pos += byteCount;
}
}
- 現(xiàn)在可以來看看Buffer里面是怎么處理數(shù)據(jù)的了倦西,就挑一個(gè)read方法,其實(shí)就是直接將傳入的byte數(shù)據(jù)copy到了segment里面能真,這里又出來了一個(gè)新的類SegmentPool
@Override public int read(byte[] sink, int offset, int byteCount) {
checkOffsetAndCount(sink.length, offset, byteCount);
Segment s = head;
if (s == null) return -1;
int toCopy = Math.min(byteCount, s.limit - s.pos);
System.arraycopy(s.data, s.pos, sink, offset, toCopy);
s.pos += toCopy;
size -= toCopy;
if (s.pos == s.limit) {
head = s.pop();
SegmentPool.recycle(s);
}
return toCopy;
}
- SegmentPool就是一個(gè)回收池~~,讀取和寫入不斷的回收利用,同一個(gè)byte[]多次利用舟陆。
最后貼一個(gè)okio的整個(gè)繼承圖吧
Nothing is certain in this life. The only thing i know for sure is that. I love you and my life. That is the only thing i know. have a good day
:)