美文网首页
Okio简单分析

Okio简单分析

作者: 大批 | 来源:发表于2017-03-11 21:47 被阅读170次

    Okio的传送门

    https://github.com/square/okio


    了解Okio之前先了解一个装饰者模式(就是java io的思路)

    • 接下来简单模拟一个io操作
    • 定义一个读取数据的接口,返回byte[ ]BytesReader
    • 定义一个它的实现类,用来读取byte[]BytesReaderImpl
    • 定义一个读取String的接口,用来读取StringReader
    • BytesReaderImpl进行装饰,,让装饰类支持读取StringStringReaderImpl
    继承关系图
    • 看看具体的代码实现吧 _( 比较简单)
      ByteReader && ByteReaderImpl
     public interface BytesReader {
           byte[] readBytes(); //定义一个读取byte[]的数组
     }
    
     public class BytesReaderImpl implements BytesReader {
       @Override
       public byte[] readBytes() {
           String str = "仅仅就是用来测试的字符串^_^...";
           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();
          }
      }
    

    现在来看看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里面有一个,Source接口定义了读取数据的接口,类似InputStream
    • okio里面还有一个,Sink接口定义了写数据的接口,类似OutputStream
    • Source和Sink有两个实现类,BufferedSourceBufferedSink,这两个实现类定义了很多方法,包括文件的读写,字符串的读写,流的读写 等
    • BufferedSource 和 BufferedSink有两个之类,RealBufferedSource和RealBufferedSink**,实际用的时候其实是用的这两个实现类
    • Okio这个类提供了很多静态方法,简化上面这些类的创建操作

    下面开始分析源代码

    • 首先有一点是明白的,就是Source和Sink这两个类定义了顶层接口,一个用来读数据,一个用来写数据。
      Source && Sink

      public 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分别有一个实现接口BufferedSource(接口)和BufferedSink(接口),这两个接口就定义了更加偏向应用的常用接口,可以看到不管是读还是写都支持常见的类型,基本类型,String,而且Source接口和Sink接口接受的参数都是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接受的参数都是Buffer [okio.Buffer],所以说Buffer在okio的读写种起着媒介(比较重要)的作用,Buffer的代码有点多(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其实既有读的功能也有写的功能,但是我们的程序里面其实是可以像调用java io的api一样,因为里面包装了一个OutputStream和InputStream ,这里还引入了一个新的对象,**Segment**
    
    • 有了大概的了解,就来看看继承图


      okio部分类的继承图

    继续啊

    • 我们构造BufferedSource是使用的是Okio.buffer(Okio.sink(fileOut));
      • 首先来看Okio.sink(fileOut),其实内部就一句,return sink(new FileOutputStream(file));,构造了一个FileOutputStream并调用了,sink(OutputStream out)方法,最终调用了sink(OutputStream out, Timeout 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");
      
        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方法,这个方法里面实际就是实例化了一个RealBufferedSink对象并返回。代码就不贴了,说哈RealBufferedSink大概做了些什么,首先是RealBufferedSink里面包含了一个Buffer(可读可写)对象,在调用RealBufferedSink的时候,实际上就是调用的Buffer对象的write方法。

    • BufferedSource和BufferedSink的处理是类似的这里就不啰嗦了。


    小结前面提到的流程

    • 在构造BufferedSource的时候会传递一个Source到Okio.buffer方法里面,而这个Source是一个匿名内部类来实例化的,并且里面使用FileInputStream去读取数据,然后吧数据保存到传入的Buffer参数里面,而这个Buffer是支持读写的。所以BufferedSource读取Buffer里面的数据,Buffer获取从FileInputStream里面的数据。从这里就可以看出来,Okio效率高就是这个Buffer在起作用,前面大概说了哈Buffer,它里面还有一个重要的对象还没有说Segment

    继续哈

    • Segment对象,Segment的源码不是很多,实现的实现其实就是一个双向链表。里面定义了一个byte[]和前一个节点的引用以及后一个节点的引用
    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;
      }
    }
    
    • 现在可以来看看Buffer里面是怎么处理数据的了,就挑一个read方法,其实就是直接将传入的byte数据copy到了segment里面,这里又出来了一个新的类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就是一个回收池~~,读取和写入不断的回收利用,同一个byte[]多次利用。

    最后贴一个okio的整个继承图吧

    点击查看大图

    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

    :)

    相关文章

      网友评论

          本文标题:Okio简单分析

          本文链接:https://www.haomeiwen.com/subject/vpxzgttx.html