OkIo介绍

简介:上一篇介绍了okhttp3的设计原理,大家也知道okhttp3必须和okio一起使用,所以本篇介绍一下okio的设计原理,及设计优势。


Okio有两个好处,

1.装饰模式的设计,使得扩展更加方便

2.Segment的设计,是的减少GC回收内存的浪费


下面针对这两个方面进行介绍,

首先来说装饰模式,原型如下:

OkIo介绍_第1张图片

这个可以看出来,RealBufferedSource是装饰类,GzipSource是基础构件,将其传入RealBufferedSource中,在不改变GzipSource代码逻辑的前提下,完成功能的添加,

注意:这只是一部分功能,实时上GzipSource,ForwardingSourct同样也是装饰类,他的内部还可以拥有其他的source类,只是为了大家了解整体,简化之后如上图,实际持有效果如下:

RealBufferedSource  ->  GzipSource -> RealBufferedSource  -> new Source(socket.getInputStream)

通过多层的装饰,最终达到封装各种功能的结果,典型的装饰模式。

sink的结构图类似。


第二部分:关于segment的服用:

我们先看看log,四次网络请求,打出如下log,每次take处理的地址,可以看到每次会有多次复用,

SegmentPool.java

  static Segment take() {
    synchronized (SegmentPool.class) {
      if (next != null) {
        Segment result = next;
        next = result.next;
        result.next = null;
        byteCount -= Segment.SIZE;
        Log.v("XPC","result="+result);
        return result;
      }
    }
    return new Segment(); // Pool is empty. Don't zero-fill while holding a lock.
  }


05-12 16:40:01.478 5743-5774/materialdesign.okhttptest V/XPC: result=okio.Segment@b789fc9
05-12 16:40:01.718 5743-5774/materialdesign.okhttptest V/XPC: result=okio.Segment@3267be2
05-12 16:40:01.718 5743-5774/materialdesign.okhttptest V/XPC: result=okio.Segment@3267be2
05-12 16:40:01.928 5743-5774/materialdesign.okhttptest V/XPC: result=okio.Segment@3267be2
05-12 16:40:07.038 5743-6066/materialdesign.okhttptest V/XPC: getget  
05-12 16:40:07.038 5743-6066/materialdesign.okhttptest V/XPC: result=okio.Segment@d29a192
05-12 16:40:07.048 5743-6066/materialdesign.okhttptest V/XPC: result=okio.Segment@d29a192
05-12 16:40:07.148 5743-6066/materialdesign.okhttptest V/XPC: result=okio.Segment@d29a192
05-12 16:40:07.158 5743-6066/materialdesign.okhttptest V/XPC: result=okio.Segment@2e07763
05-12 16:40:14.488 5743-6289/materialdesign.okhttptest V/XPC: getget  
05-12 16:40:14.488 5743-6289/materialdesign.okhttptest V/XPC: result=okio.Segment@2e07763
05-12 16:40:14.488 5743-6289/materialdesign.okhttptest V/XPC: result=okio.Segment@2e07763
05-12 16:40:14.598 5743-6289/materialdesign.okhttptest V/XPC: result=okio.Segment@2e07763
05-12 16:40:14.598 5743-6289/materialdesign.okhttptest V/XPC: result=okio.Segment@437bd89
05-12 16:40:23.908 5743-6596/materialdesign.okhttptest V/XPC: getget  
05-12 16:40:23.908 5743-6596/materialdesign.okhttptest V/XPC: result=okio.Segment@437bd89
05-12 16:40:23.918 5743-6596/materialdesign.okhttptest V/XPC: result=okio.Segment@437bd89
05-12 16:40:24.028 5743-6596/materialdesign.okhttptest V/XPC: result=okio.Segment@437bd89
05-12 16:40:24.028 5743-6596/materialdesign.okhttptest V/XPC: result=okio.Segment@e8e1af9


下面来依次介绍几个知识点:

1.Segment.java

双向链表的实现类

push:将segment插入到当前的后面。

OkIo介绍_第2张图片

pop:移除当前的元素

OkIo介绍_第3张图片


SegmentPool.java

数据池的复用,take是从池子中获取数据,recycle则是将segment再放入池子中,供其他人使用。

  static Segment take() {
    synchronized (SegmentPool.class) {
      if (next != null) {  //如果有可使用的segment
        Segment result = next;   //将当前的segment取出来,
        next = result.next;     //数据池指向下一个,
        result.next = null;     //当前segment的下一个指定为null,
        byteCount -= Segment.SIZE;   //总容量减少,
        return result;     //将取出的segment取出
      }
    }
    return new Segment(); // Pool is empty. Don't zero-fill while holding a lock.
  }

  //这里并不是回收,而是将segment放到数据池中
  static void recycle(Segment segment) {
    if (segment.next != null || segment.prev != null) throw new IllegalArgumentException();
    if (segment.shared) return; // This segment cannot be recycled.
    synchronized (SegmentPool.class) {
      if (byteCount + Segment.SIZE > MAX_SIZE) return; // 池子满了,直接抛弃掉,
      byteCount += Segment.SIZE;  //池子增大表记
      segment.next = next;       //将segment放到池子的第一个位置
      segment.pos = segment.limit = 0;  //重置segment的标示位
      next = segment;       //当前位置,设置为 segment
    }
  }

通过这个池子中数据的复用,不需要每次都进行垃圾的GC回收,这样提高的内存使用率。


这个只是整体比较重要的一些思想的设计,其实还有很重要的部分,我们没有介绍,Buffer是很核心的内容,但是比较细节,篇幅也较大,网上的内容也比较多,我就不介绍了。



参考文章:

OkIo :https://blog.piasy.com/2016/08/04/Understand-Okio/

Okio源码学习  : http://xuzhengchao.com/java/okio.html 

你可能感兴趣的:(OkIo介绍)