OKHttp源码分析---OKIO

简介
JavaNIO和阻塞I/O
  1. 阻塞I/O通信模式:调用InputStream.read()方法时是阻塞的,它会一直等到数据来时才返回。
  2. NIO通信模式:在JDK1.4开始,是一种非阻塞I/O,在Java NIO的服务器由一个专门的线程来处理所有I/O事件,并负责分发。线程之间通讯通过wait和notify等方式。
okio

okio是由square公司开发的,它补充了java.io和java.nio的不足,以便能够更加方便,快速的访问,存储和处理数据。OKHttp底层也是用该库作为支持,而且okio使用起来很简单,减少了很多io操作的基本代码,并且对内存和cpu使用做了优化,它的主要功能封装在ByteString和Buffer这两个类中。

okio的作者认为,java的JDK对字节流和字符流进行分开定义这一事情,并不是那么优雅,所以早okio并不进行这样的划分,具体的做法就是把比特数据交给Buffer管理,然后Buffer实现BufferedSource和BufferedSink这两个接口,最后通过调用buffer响应的方法对数据进行编码。

Okio的使用

假设有一个text.txt文件,我们用Okio将它读出来。
读取文件的步骤是首先要拿到一个输入流,okio封装了许多输入流,统一使用的方法重载source方法转换成一个source,然后使用buffer方法包装成BufferedSource,这个里面提供了流的各种操作,读String,读byte,short等,甚至是16进制数。这里直接读出文件的内容,十分简单,代码如下:

public static void main(String[] args){
    
        File file = new File("test.txt");
        try {
    
            readFile(new FileInputStream(file));
        }catch (IOException e){
    
            e.printStackTrace();
        }
    }

    public static void readFile(InputStream inputStream)throws IOException{
    
        BufferedSource source = Okio.buffer(Okio.source(inputStream));
        String s = source.readUtf8();
        System.out.println(s);
        source.close();
    }

大体流程:

  1. 构建缓冲区,缓冲源对象
  2. 读写操作
  3. 关闭缓冲池
Sink和Source

在JDK里面有InputStream和OutputStream两个接口, Source和Sink类似于InputStream和OutoputStream,是io操作的顶级接口类,这两个接口均实现了Closeable接口。所以可以把Source简单的看成InputStream,Sink简单看成OutputStream。

在这里插入图片描述

其中Sink代表的是输出流,Source代表的是输入流:

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.

大概翻译:
接收字节流。使用此接口可以在任何需要写入数据的地方写入数据:网络,储存或内存中的缓冲区。Sink可能分层的去转换接收到的数据,像压缩,加密,限制或添加协议框架。

和OutputStream的比较:

		{
    @code OutputStream} requires multiple layers when emitted data is
  heterogeneous: a {
    @code DataOutputStream} for primitive values, a {
    @code
  BufferedOutputStream} for buffering, and {
    @code OutputStreamWriter} for
  charset encoding. This class uses {
    @code BufferedSink} for all of the above.
 
		Sink is also easier to layer: there is no {
    @linkplain
  java.io.OutputStream#write(int) single-byte write} method that is awkward to
  implement efficiently.

OutputStream在发出数据的是混合的时候,,需要多层:DataOutputStream作为原始的值,BufferedOutputStream用于缓冲,OutputStream用于编码。而Sink使用BufferedSink综合了上面所有。

Sink也更加容易分层,它没有OutputStream.write(int),这种很难有效实施的对单字节写入的方法。

public interface Sink extends Closeable, Flushable {
    
  /** Removes {@code byteCount} bytes from {@code source} and appends them to this. */
  //定义基础的write操作,该方法将字节写入Buffer
  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.
   */
  @Override void close() throws IOException;
}

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. Sources may
  be layered to transform supplied data, such as to decompress, decrypt, or
  remove protocol framing.

提供字节流,使用此接口从任何地方读取数据:从网络,存储,或者内存中的缓冲区。Source可能分层以转换提供的数据,像解压缩,解密或移除协议框架。

和InputStream的比较:

{
    @code InputStream} requires multiple layers when consumed data is
  heterogeneous: a {
    @code DataInputStream} for primitive values, a {
    @code
  BufferedInputStream} for buffering, and {
    @code InputStreamReader} for
  strings. This class uses {
    @code BufferedSource} for all of the above.
 
  Source avoids the impossible-to-implement {
    @linkplain
  java.io.InputStream#available available()} method. Instead callers specify
  how many bytes they {
    @link BufferedSource#require require}.
 
 Source omits the unsafe-to-compose {
    @linkplain java.io.InputStream#mark
 mark and reset} state that's tracked by {
    @code InputStream}; instead, callers
  just buffer what they need.
 
 When implementing a source, you don't need to worry about the {
    @linkplain
   java.io.InputStream#read single-byte read} method that is awkward to implement efficiently
  and returns one of 257 possible values.
 
 And source has a stronger {
    @code skip} method: {
    @link BufferedSource#skip}
  won't return prematurely.
 

简单来说,Source对于混合数据,不用考虑它的分层,因为它综合了,Source不能使用InputStream中的available()方法,而是用require去指定需要多少字节;Sourse省略了不安全写入的InputStream的mark方法,相反,只是缓冲呼叫着需要的东西;Source不用担心读取一个字节的时候,InputStream可能会返回 256个值,Source使用的sikp方法,不会过早的返回。

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.
   */
    // 定义基础的read操作,该方法将字节写入Buffer
  long read(Buffer sink, long byteCount) throws IOException;

  /** Returns the timeout for this source. */
  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;
}

Sink和Source只是定义了很少的方法,一般在使用的过程中,不会直接用它,而是用BufferedSink和BufferedSource这两个接口,它们有对Sink和Source进行了封装,它们的实现类是RealBufferedSink和RealBufferedSource

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;
  }

看源码可知ReadlBufferedSink类中有两个主要参数,一个是新建的Buffer对象,一个是Sink对象。虽然这个类叫做ReadlBufferedSink,但是实际上这个只是保存一个Buffer独享的代理而已,真正的实现都在Buffer中实现的。

@Override public void write(Buffer source, long byteCount)
      throws IOException {
    
    if (closed) throw new IllegalStateException("closed");
    buffer.write(source, byteCount);
    emitCompleteSegments();
  }

  @Override public BufferedSink write(ByteString byteString) throws IOException {
    
    if (closed) throw new IllegalStateException("closed");
    buffer.write(byteString);
    return emitCompleteSegments();
  }

这个Buffer类,等会分析。
刚才我们可以看到Sink接口中有一个Time类,这个就是Okoio实现超时机制的接口,用于包装IO操作的稳定性。

Segment和segmentPool
Segment

Segment字面的意思就是片段,Okio将数据也就是Buffer分割成一块块的片段,内部维护固定长度的byte[ ]数组,同时Segment拥有前面节点和后面节点,构成了一个双向循环链表:

在这里插入图片描述

这样采取分片使用链表连接,片中使用数组储存,兼具读的连续性和写的可插入性,对比单一使用链表或者数组,是一种这种的方案,读写更快,而且有一个好处,根据需求改动分片的大小来权衡读写的业务操作,另外,segment也有一些内置IDE优化操作:

还是先看官网描述:


  A segment of a buffer.
  一个buffer的片段
  
 Each segment in a buffer is a circularly-linked list node referencing the following and
  preceding segments in the buffer.
  每个片段是双向循环连接的一个节点
 
Each segment in the pool is a singly-linked list node referencing the rest of segments in the
pool.
 每个segment都是唯一的
 
 	The underlying byte arrays of segments may be shared between buffers and byte strings. When a
segment's byte array is shared the segment may not be recycled, nor may its byte data be changed.
The lone exception is that the owner segment is allowed to append to the segment, writing data at
{
    @code limit} and beyond. There is a single owning segment for each byte array. Positions,
 limits, prev, and next references are not shared.
 segments的byte数组可以在buffers和字节串之间共享。当一个segment的byte数组是共享的,
 那么这个segment是不可回收的,也不能改变byte数组的数据。除非byte数组的所有者是自己
// 每一个segment所含数据的大小,固定的
    static final int SIZE = 8192;
     // Segments 用分享的方式避
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值