09 | Android 高级进阶(源码剖析篇) Square 高效易用的 IO 框架 okio(二)

作者简介:ASCE1885, 《Android 高级进阶》作者。
本文由于潜在的商业目的,未经授权不开放全文转载许可,谢谢!
本文分析的源码版本已经 fork 到我的 Github

向日葵

前文中我们了解了 Java I/O 的历史和 okio 的基本概念,本文将开始 okio 中的输入流 Source 的剖析,首先看下它的类结构图:

类结构图

可以看到,层次还是挺丰富的,平时用的最多的自然是 Source --> BufferedSource --> RealBufferedSourceBuffer。既然是 Source 专场,那么我们就先来聊聊 Source 的基本概念。

注意这里的 Source 是一个统称的概念,并不是指上图中的 Source 这个接口。

Source 是对输入字节流的封装,通过它提供的接口,我们可以从网络,本地存储或者内存缓存中读取数据,同时它也可以提供对数据的转换操作,例如解压,解密等。和 java.io 的 InputStream 相比,两者实现的功能其实是一样的。但 InputStream 处理异构数据时需要多种输入流,例如使用 DataInputStream 处理基本数据类型的数据, 使用 BufferedInputStream 增加缓存能力,使用 InputStreamReader 用来读取字符串数据等,而 Source 体系中的 BufferedSource 能够满足所有这些需求。接下来我们就来细细品味 Source 体系中的各个成员,首先从 Source 接口开始。

Source 接口

Source 是输入流,存在需要关闭的数据,因此,Source 接口继承了 Closeable 并在 close 方法中关闭资源。同时定义了从 Buffer 中读取指定个数的字节信息的 read 方法,相比 InputStream,Source 接口定义了 timeout 方法实现超时机制,代码如下所示:

public interface Source extends Closeable {
  long read(Buffer sink, long byteCount) throws IOException;
  
  Timeout timeout();
  
  @Override void close() throws IOException;
}

BufferedSource 接口

BufferedSource 接口在 Source 接口的基础上,主要做了两件事:

  • 利用内部定义的缓冲区 Buffer 来提高数据读取的性能
  • 定义一系列读取基本数据类型和字符串类型的方法,例如 readByte 读取一个字节的信息,readShort 读取两个字节的信息,readInt 读取四个字节的信息,readLong 读取八个字节的信息,readByteString 读取字节信息并转换为 ByteString 类型,readByteArray 读取字节数组信息,readUtf8 读取字节信息并转换为 UTF-8 编码的字符串等。

BufferedSource 作为接口,具体的实现在它的实现类 RealBufferedSource 类和 Buffer 类中。这里需要说明一下,RealBufferedSource 内部也是使用 Buffer 类提供的接口来实现底层的数据读取等操作,我们可以将 RealBufferedSource 当作对 Buffer 类的一个代理,它在 Buffer 类提供的方法基础上增加一些校验等逻辑。这三者的关系如下图所示:

三者关系图

RealBufferedSource 类

既然 RealBufferedSource 类是代理类,那么可以预知它本身的代码逻辑不会很复杂,例如在实现 Source 接口提供的 read 方法时,它首先对入参进行校验,当参数为 null 或者不符合逻辑时抛出相应的异常,接着确保内部缓冲区 buffer 中有数据,然后根据缓存区 buffer 中的字节数和 read 方法想读取的字节数决定最终可以读取的字节数,最后调用 Buffer 类的实现的 Source 接口的同名方法,代码如下所示:

/**
* 将数据从输入流 source 中读取到缓冲区 buffer,然后经过 buffer 将数据写入输出流 sink 中
*/
@Override public long read(Buffer sink, long byteCount) throws IOException {
    // 入参校验
    if (sink == null) throw new IllegalArgumentException("sink == null");
    if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
    if (closed) throw new IllegalStateException("closed");

    // 确保缓存区 buffer 中有数据,一次最多从 source 中读取一个 Segment 大小的数据块
    if (buffer.size == 0) {
      long read = source.read(buffer, Segment.SIZE);
      if (read == -1) return -1;
    }

    // 决定最终可以读取的字节大小
    long toRead = Math.min(byteCount, buffer.size);
    
    return buffer.read(sink, toRead);
  }

这个类中其他方法也都是类似的校验逻辑,我们就不细说了,但有一段代码除外,那就是将 Source 转换为 InputStream 的逻辑,它位于 inputStream 方法中,这个方法也是实现自 BufferedSource 接口的。那么如何实现 Source 和 InputStream 的转换呢?由于 Source 是 okio 自身定义的概念,因此,想要转换成 InputStream 也就只能用最直接的方法,就是创建一个 InputStream,我们知道 InputStream 是一个抽象类,那么继承这个抽象类并实现它的抽象方法就可以了,当然,也可以用匿名内部类的方式,RealBufferedSource 中的 inputStream 方法就是使用匿名内部类的方式,也就是直接 new 出一个 InputStream,并实现它的抽象方法和重写部分方法,代码实现中,依然主要是作校验操作,具体数据读操作交由 Buffer 类。

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

推荐阅读更多精彩内容