Okio 框架
就问你服不平?
而且你看哈,这些都是平常业务开辟中最常见的根本范例,原生 IO 把它们都拆分开了,让标题复杂化了。反观 Okio 直接在 BufferedSource 和 BufferedSink 中聚合了原生 IO 中根本的功能,而不再须要区分字节、字符、字节数组、字符数组、基础范例等等装饰器,确实让框架更加精简。
BufferedSource.kt
actual interface BufferedSource : Source, ReadableByteChannel { actual val buffer: Buffer // 读取 Int @Throws(IOException::class) actual fun readInt(): Int // 读取 String @Throws(IOException::class) fun readString(charset: Charset): String ... fun inputStream(): InputStream}BufferedSink.kt
actual interface BufferedSink : Sink, WritableByteChannel { actual val buffer: Buffer // 写入 Int @Throws(IOException::class) actual fun writeInt(i: Int): BufferedSink // 写入 String @Throws(IOException::class) fun writeString(string: String, charset: Charset): BufferedSink ... fun outputStream(): OutputStream}2.4 RealBufferedSink 与 RealBufferedSource
BufferedSource 和 BufferedSink 照旧接口,它们的真正的实现类是 RealBufferedSource 和 RealBufferedSink。可以看到,在实现类中会创建一个 Buffer 缓冲区,在输入和输出的时间,都会借助 “Buffer 缓冲区” 镌汰体系调用次数。
RealBufferedSource.kt
internal actual class RealBufferedSource actual constructor( // 装饰器模式 @JvmField actual val source: Source) : BufferedSource { // 创建输入缓冲区 @JvmField val bufferField = Buffer() // 带缓冲地读取(全部数据) override fun readString(charset: Charset): String { buffer.writeAll(source) return buffer.readString(charset) } // 带缓冲地读取(byteCount) override fun readString(byteCount: Long, charset: Charset): String { require(byteCount) return buffer.readString(byteCount, charset) }}RealBufferedSink.kt
internal actual class RealBufferedSink actual constructor( // 装饰器模式 @JvmField actual val sink: Sink) : BufferedSink { // 创建输出缓冲区 @JvmField val bufferField = Buffer() // 带缓冲地写入(全部数据) override fun writeString(string: String, charset: Charset): BufferedSink { buffer.writeString(string, charset) return emitCompleteSegments() } // 带缓冲地写入(beginIndex - endIndex) override fun writeString( string: String, beginIndex: Int, endIndex: Int, charset: Charset ): BufferedSink { buffer.writeString(string, beginIndex, endIndex, charset) return emitCompleteSegments() }}至此,Okio 根本框架分析结束,用一张图总结:
Okio 框架
3. Okio 的缓冲区计划
Buffer.kt
actual class Buffer : BufferedSource, BufferedSink, Cloneable, ByteChannel { // 缓冲区(Segment 双向链表) @JvmField internal actual var head: Segment? = null // 缓冲区数据量 @get:JvmName("size") actual var size: Long = 0L internal set override fun buffer() = this actual override val buffer get() = this}对比 BufferedInputStream:
BufferedInputStream.java
public class BufferedInputStream extends FilterInputStream { // 缓冲区的默认大小(8KB) private static int DEFAULT_BUFFER_SIZE = 8192; // 输入缓冲区(固定长度的数组) protected volatile byte buf[]; // 有用数据起始位,也是读数据的起始位 protected int pos; // 有用数据量,pos + count 是写数据的起始位 protected int count; ...}3.4 Segment 片断与 SegmentPool 对象池
Segment 中的字节数组是可以 “共享” 的,当数据从一个缓冲区转移到另一个缓冲区时,可以共享数据引用,而不肯定须要拷贝数据。
Segment.kt
internal class Segment { companion object { // 片断的默认大小(8KB) const val SIZE = 8192 // 最小共享阈值,凌驾 1KB 的数据才会共享 const val SHARE_MINIMUM = 1024 } // 底层数组 @JvmField val data: ByteArra // 有用数据的起始位,也是读数据的起始位 @JvmField var pos: Int = 0 // 有用数据的结束位,也是写数据的起始位 @JvmField var limit: Int = 0 // 共享标志位 @JvmField var shared: Boolean = false // 宿主标志位 @JvmField var owner: Boolean = false // 后续指针 @JvmField var next: Segment? = null // 前驱指针 @JvmField var prev: Segment? = null constructor() { // 默认构造 8KB 数组(为什么默认长度是 8KB) this.data = ByteArray(SIZE) // 宿主标志位 this.owner = true // 共享标志位 this.shared = false }}别的,Segment 还使用了对象池计划,被回收的 Segment 对象会缓存在 SegmentPool 中。SegmentPool 内部维护了一个被回收的 Segment 对象单链表,缓存容量的最大值是 MAX_SIZE = 64 * 1024,也就相称于 8 个默认 Segment 的长度:
SegmentPool.kt
// object:全局单例internal actual object SegmentPool { // 缓存容量 actual val MAX_SIZE = 64 * 1024 // 头节点 private val LOCK = Segment(ByteArray(0), pos = 0, limit = 0, shared = false, owner = false) ...}Segment 表现图
4. 总结