简介:上一篇介绍了okhttp3的设计原理,大家也知道okhttp3必须和okio一起使用,所以本篇介绍一下okio的设计原理,及设计优势。
Okio有两个好处,
1.装饰模式的设计,使得扩展更加方便
2.Segment的设计,是的减少GC回收内存的浪费
下面针对这两个方面进行介绍,
首先来说装饰模式,原型如下:
这个可以看出来,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插入到当前的后面。
pop:移除当前的元素
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
}
}
这个只是整体比较重要的一些思想的设计,其实还有很重要的部分,我们没有介绍,Buffer是很核心的内容,但是比较细节,篇幅也较大,网上的内容也比较多,我就不介绍了。
参考文章:
OkIo :https://blog.piasy.com/2016/08/04/Understand-Okio/
Okio源码学习 : http://xuzhengchao.com/java/okio.html