Okio简单分析

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

:)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 前言 Okio是一款轻量级IO框架,由安卓大区最强王者Square公司打造,是著名网络框架OkHttp的基石。Ok...
    开发者小王阅读 13,974评论 5 50
  • 1.OkHttp源码解析(一):OKHttp初阶2 OkHttp源码解析(二):OkHttp连接的"前戏"——HT...
    隔壁老李头阅读 10,496评论 24 42
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • square在开源社区的贡献是卓越的,这里是square在Android领域贡献的开源项目。 1. okio概念 ...
    王英豪阅读 1,122评论 0 2
  • 现在经常回忆小时候,仿佛昨天刚下课,今天突然就穿越过来。中间那段时间刷刷刷地飞过去,曾经以为很久远的事情突然就变得...
    木目木目阅读 266评论 0 0