今天来扒一扒Square
公司的IO
流的库Okio
,现在越来越多Android
项目都在使用Square
公司的网络开源全家桶,即 Okio + OkHttp + Retrofit
。这三个库的层级是从下网上来看,Okio
用来处理IO
流,OkHttp
用来实现Http
协议,Retrofit
用来做Android
端的网络使用接口,关于Retrofit
,之前写过源码分析。但是相对于Retrofit
和OkHttp
,Okio
就比较低调,因为它偏底层,大部分同学对它可能不太熟悉,我们今天就来看看这个幕后英雄吧。
Square
功德无量,著名的JakeWharton
大神之前就一直在这家公司(据说今年7月份离职了)。
一 为什么需要Okio
首先我们需要强调Okio
是一个Java
库,所以它的底层流肯定都是JavaIO
中定义的基础流。基础流指的是Java
中对于从不同数据来源抽象的流,比如是FileInputStream ByteInputStream PipedInputStream
等,Okio
只是优化了Java IO
库中对于基础流包装的API
。
1.1 更加简洁
我们经常说Java
的IO
库是JDK
设计的比较精妙的一个API
,由于使用了装饰者模式,大大减少了类的数目。然而即便如此,大家使用时仍然感觉比较麻烦,比如我们想要从一个文件中读取出来一个int数据,我们至少要创建如下几个对象:
FileInputStream //用于打开文件流
BufferedInputStream //对FileInputStream做装饰,添加buffer功能,避免频繁IO
DataInputStream //用于将字节流转化成Java基本类型
此时,通过DataInputStream.readInt()
就可以读出来一个int了。但是机智的你已经发现了,我们需要和至少三个跟输入相关的类打交道,而Okio把这些都做了集中处理,你可能只需要一个类就可以很方便的进行以上的各种操作了。
1.2 提高性能
首先okio
内部用一个Segment
对象来描述内存数据,Segment
对象中就有byte[]
作为数据的载体。对于Segment
来说,Okio
不是每次都去创建,而且通过一个对象池来做复用,这样就可以减少对象创建,销毁代价,实际上也可以减少byte[]
数组zero-fill
的代价。关于Segment
,我们会在第四章专门进行介绍。
其次,Okio
中多个流之间的数据是可以共享的,而不需要进行内存拷贝。我们举个栗子,如果我使用Java IO
从一个文件中读取数据,写入另外一个文件中,代码大致如下:
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(new File("in.file")));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(new File("out.file")));
byte[] bytes = new byte[2048];
int count;
while ((count = bufferedInputStream.read(bytes)) != -1) {
bufferedOutputStream.write(bytes,0,count);
}
bufferedOutputStream.flush();
BufferedInputStream
内部有个缓存,每次read
的时候,先看看自己缓存中是否满足读取方的需要,如果满足,直接从内部缓存中做内存copy
到读取方传入的byte
数组(步骤一);如果不满足,调用