在上篇文章中,我们知道了一些 Java IO 的概念,也了解了 okio 的用法,现在我们来分析一下源码

Okio

我们回到上篇的代码:

 Source source = Okio.source(new File(mPath));String read = Okio.buffer(source).readString(Charset.forName("utf-8"));

显而易见 Okio 是个入口类,里面方法如下:

Okio 里面的代码过一遍的话,就会发现其实 Okio 就是个工厂类,它的主要工作就是将 OutputStreamInputStream 转成 SinkSource

转换方法也是十分简单粗暴,就是直接 new 出来,这里就只贴 Source 的生成方法:

private static Source source(final InputStream in, final Timeout timeout) {if (in == null) throw new IllegalArgumentException("in == null");if (timeout == null) throw new IllegalArgumentException("timeout == null");return new Source() {@Overridepublic long read(Buffer sink, long byteCount) throws IOException {if (byteCount < 0)throw new IllegalArgumentException("byteCount < 0: " + byteCount);if (byteCount == 0) return 0;try {timeout.throwIfReached();Segment tail = sink.writableSegment(1);int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit);int bytesRead = in.read(tail.data, tail.limit, maxToCopy);if (bytesRead == -1) return -1;tail.limit += bytesRead;sink.size += bytesRead;return bytesRead;} catch (AssertionError e) {if (isAndroidGetsocknameError(e)) throw new IOException(e);throw e;}}@Overridepublic void close() throws IOException {in.close();}@Overridepublic Timeout timeout() {return timeout;}@Overridepublic String toString() {return "source(" + in + ")";}};}

Sink 和 Source

在 okio 中 ,SinkSource 分别是用来替代 Java 的 OutputStreamInputStream ,我们可以理解为

Sink 就是 输出流,Source 就是输入流,它们基本都是对称的,因此这里就继续用 Source 来进行分析了

先来看下 Source 的代码:

public interface Source extends Closeable {/*** 读取流里面的数据* 返回读取的字节数,如果是-1则说明已经读取完成*/long read(Buffer sink, long byteCount) throws IOException;/** 超时机制 */Timeout timeout();/*** 关闭流并释放流所拥有的资源*/@Override void close() throws IOException;
}

Source 中只包括了一些简单的方法,不过有一个需要注意的 timeout() ,这个是超时机制,后面再讲

我们来看下它的子类有那些:

可以看到还是有不少子类的,我们先来看一些熟悉的吧,BufferedSourceBuffer 都是上篇文章出现过的,那就先从这两个开始吧

BufferedSource

BufferedSource 接口在 Source 接口的基础上 多了下面这一堆方法:

BufferedSource 增加了许多读的方法,但是它依然还是个接口,它的真正实现类是 RealBufferedSource

这一点,可以在 okio 这个类里面找到依据:

bufferokio 的入口方法,它的作用就是生成一个 RealBufferedSource

###RealBufferedSource

虽然 RealBufferedSource 这个类名带有 real 这个单词,但是实际上它只是一个代理类和 Source 的一个装饰类,可以从下面的代码看出(下面的 SourceOkio 中生成的):

基本上,在 RealBufferedSource 实现了 BufferedSource 接口的方法,实际上都是调用了 Buffer 类中的对应方法,因此 RealBufferedSource 就仅是 Buffer 的一个代理类而已

BufferedSourceRealBufferedSourceBuffer 的关系如下图

Buffer

查看这个 Buffer 类,会发现它还同时继承了 BufferedSink ,也就是不管是 Source 还是 Sink ,最终都是要转换为 Buffer 的:

public final class Buffer implements BufferedSource, BufferedSink, Cloneable {.......//全局对象Segment head;//全局对象,用于记录流的大小,下面会用到的long size;
}

现在我们知道了 okio 最终都是要调用 Buffer 类里面方法,那我们先来看 RealBufferedSource 里面的 readString 方法吧,这个就是我们上篇调用的方法的底层实现:

String read = Okio.buffer(source).readString(Charset.forName("utf-8"));
........
//RealBufferedSource 最终调用的 buffer 类的方法@Override
public String readString(Charset charset) throws IOException {//判断编码对象是否为nullif (charset == null) throw new IllegalArgumentException("charset == null");//写入内存缓冲区中buffer.writeAll(source);//从内存内存缓冲区中读取数据return buffer.readString(charset);
}

我们先去看下 buffer.writeAll(source) 方法吧,它到底是干了什么:

@Override
public long writeAll(Source source) throws IOException {if (source == null) throw new IllegalArgumentException("source == null");long totalBytesRead = 0;//source.read(this,Segment.SIZE),这里是获取sink来进入写入操作,写入大小为Segment.SIZEfor (long readCount; (readCount = source.read(this, Segment.SIZE)) != -1; ) {totalBytesRead += readCount;}return totalBytesRead;
}

这里的 sourceOkio 类中的生成的,我们再回顾一下具体的方法代码:

@Override
public long read(Buffer sink, long byteCount) throws IOException {if (byteCount < 0)throw new IllegalArgumentException("byteCount < 0: " + byteCount);if (byteCount == 0) return 0;try{timeout.throwIfReached();//得到一个Segment对象Segment tail = sink.writableSegment(1);//获取InputStream每次读取数据的长度int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit);//InputStream.read(byte b[], int off, int len),熟悉的读取方法//tail.data,这个是数组就是一个内存缓冲区int bytesRead = in.read(tail.data, tail.limit, maxToCopy);//-1表示流读取完成了if (bytesRead == -1) return -1;tail.limit += bytesRead;//记录读取完成的流的数据大小sink.size += bytesRead;return bytesRead;} catch (AssertionError e) {if (isAndroidGetsocknameError(e)) throw new IOException(e);throw e;}
}

接着看下 buffer.readString(charset) 方法:

@Overridepublic String readString(Charset charset) {try {//又看到全局的size,这里已经记录下刚才读取的流的打下了return readString(size, charset);} catch (EOFException e) {throw new AssertionError(e);}}@Overridepublic String readString(long byteCount, Charset charset) throws EOFException {checkOffsetAndCount(size, 0, byteCount);if (charset == null) throw new IllegalArgumentException("charset == null");if (byteCount > Integer.MAX_VALUE) {throw new IllegalArgumentException("byteCount > Integer.MAX_VALUE: " + byteCount);}if (byteCount == 0) return "";//获取全局的Segment对象Segment s = head;if (s.pos + byteCount > s.limit) {//如果内存缓冲区的流的长度超过了限制,那么使用readByteArray方法return new String(readByteArray(byteCount), charset);}//将内存缓冲区的流(二进制数组)转为StringString result = new String(s.data, s.pos, (int) byteCount, charset);s.pos += byteCount;size -= byteCount;//回收Segmentif (s.pos == s.limit) {head = s.pop();SegmentPool.recycle(s);}//得到文本内容return result;}

我们看到如果超过了某个限制,会调用其他的方法,我们再追下去:

@Override
public byte[] readByteArray(long byteCount) throws EOFException {checkOffsetAndCount(size, 0, byteCount);if (byteCount > Integer.MAX_VALUE) {throw new IllegalArgumentException("byteCount > Integer.MAX_VALUE: " + byteCount);}//生成和流一样大小的byte数组  byte[] result = new byte[(int) byteCount];//读取流并填入byte数组中  readFully(result);return result;
}
............
@Override
public void readFully(byte[] sink) throws EOFException {int offset = 0;while (offset < sink.length) {//循环去将内存缓冲区的内存填入byte数组int read = read(sink, offset, sink.length - offset);if (read == -1) throw new EOFException();offset += read;}
}
.....
@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);//数组的内容复制方法//将Segment中的缓存的流的数据复制到byte数组中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;
}

整个 Okio 读取本地文本的数据的流程就是以上的过程了,这里我们看到了一个新的内容:Segment

它是一个内存的数据缓冲区,我们将会在下篇去分析

高效易用的okio(二)相关推荐

  1. Java Okio-更加高效易用的IO库

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/119997762 本文出自[赵彦军的博客] Java IO流学习总结一:输入输出流 ...

  2. 为你推荐一款高效的IO组件——okio

    原文:为你推荐一款高效的IO组件--okio,点击链接查看更多技术内容. 前不久,三方组件库上新了一批JS/eTS组件,其中就包括okio组件.okio是一个可应用于HarmonyOS的高效IO库,它 ...

  3. Ceres Solver: 高效的非线性优化库(二)实战篇

    Ceres Solver: 高效的非线性优化库(二)实战篇 接上篇: Ceres Solver: 高效的非线性优化库(一) 如何求导 Ceres Solver提供了一种自动求导的方案,上一篇我们已经看 ...

  4. 你真的会用 VS Code 的 Ctrl、Shift和Alt吗?高效易用的快捷键:多光标、跳转引用等轻松搞定

    你真的会用 VS Code 的 Ctrl.Shift和Alt吗?高效易用的快捷键:多光标.跳转引用等轻松搞定 使用键盘浏览代码.敲代码是一件很高效的事:主力手在鼠标和键盘之间反复移动,长时间下来,很容 ...

  5. Okio—— 更加高效易用的IO库

    在OkHttp的源码中经常能看到Okio的身影,所以单独拿出来学习一下,作为OkHttp的低层IO库,Okio确实比传统的java输入输出流读写更加方便高效.Okio补充了java.io和java.n ...

  6. Okio—— 更加高效易用的IO库,android开发网络编程

    public void readLines(File file) throws IOException { try (BufferedSource bufferedSource = Okio.buff ...

  7. Okio—— 更加高效易用的IO库,太完整了

    System.out.println(line); } } } 写文本文件 public void writeEnv(File file) throws IOException { Sink file ...

  8. 11 | Android 高级进阶(源码剖析篇) Square 高效易用的 IO 框架 okio(四)

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

  9. 超越YOLOv5,1.3M超轻量,高效易用,这个目标检测开源项目太香了!

    这个目标检测神器简直香炸了!它不仅连续登录Github全球趋势榜,拥有的全球尖端算法论文也接连登录全球技术趋势榜PaperWithCode. 这个神器就是刚刚全面升级的PaddleDetection2 ...

最新文章

  1. 在C#中使用代理的方式触发事件 (委托和事件 ) (转)
  2. VCAP-DCA 510 经验分享
  3. oc 中随机数的用法(arc4random() 、random()、CCRANDOM_0_1()
  4. 【NLP】看不懂bert没关系,用起来so easy!
  5. java http请求 乱码_怎么解决java中的http请求乱码
  6. 【LCT】网络(luogu 2173/ZJOI2011)
  7. 谷歌 colab_如何在Google Colab上使用熊猫分析
  8. java快捷键 --_Java中的快捷方式“或分配”(| =)运算符
  9. mysql join buffer_MySQL cache之join buffer的优化
  10. Array 对象-sort()
  11. 零基础Python知识点回顾(三)
  12. Unity发布ios和Android获取相机权限的相关问题
  13. 最新资料!工银亚洲开户见证业务受理网点(广东地区,除了深圳)
  14. city机器人 东京diver_东京一日游路线推荐
  15. 为什么 Go 语言能在中国这么火?
  16. vue项目PC端屏幕分辨率与窗口大小自适应
  17. 计算机视觉到底需要学什么?怎么快速入门?
  18. 北京地铁规划图_测试博客撰写增加图片的办法
  19. 聊聊GIS中那些坐标系(转)
  20. 什么是持续集成的自动化测试

热门文章

  1. 00后的程序员都开始期望年薪百万
  2. 计算机系统节能减排,节能减排,我们怎么做?
  3. python设置excel套打_用强大的Python处理海量的Excel,工作从未如此简单
  4. Kali模拟-在Android端窃取信息
  5. mysql select table_MySQL中的CREATE TABLE……SELECT语句
  6. Vue动态添加、删除对象属性
  7. 累死累活干不过一个写PPT的(如何量化工作成果)
  8. js 模板字符串里面用换行符不起作用
  9. 9158意欲赴美上市 恐遭山寨公司冲击
  10. Vidahouse独创“DNA技术”颠覆你的设计观