android okio使用方法,Android 开源框架 Okio 原理剖析「建议收藏」

android okio使用方法,Android 开源框架 Okio 原理剖析「建议收藏」Retrofit,OkHttp,Okio是Square团队开源的安卓平台网络层三板斧,它们逐层分工,非常优雅地解决我们对网络请求甚至更广泛的I/O操作的需求。其中最底层的Okio堪称小而美,功能也更基础,应用更广泛。这次我们就对它进行一个详细的分析。本文的分析基于Okio截至2016.8.4的最新源码,非常建议大家下载Okio源码之后,跟着本文,过一遍源码。1,概览和分析…

大家好,又见面了,我是你们的朋友全栈君。

Retrofit,OkHttp,Okio 是 Square 团队开源的安卓平台网络层三板斧,它们逐层分工,非常优雅地解决我们对网络请求甚至更广泛的 I/O 操作的需求。其中最底层的 Okio 堪称小而美,功能也更基础,应用更广泛。这次我们就对它进行一个详细的分析。本文的分析基于 Okio 截至 2016.8.4 的最新源码,非常建议大家下载 Okio 源码之后,跟着本文,过一遍源码。

1,概览

和分析 Retrofit 和 OkHttp 时不同,这次我们不是直接上来就开始看代码,我们先看一下它的官方介绍,对它有一个感性的认识,这也正是我们在进行技术选型时首先应该做的事情。

Okio 补充了 java.io 和 java.nio 的内容,使得数据访问、存储和处理更加便捷。

它的主要功能都被封装在 ByteString 和 Buffer 这两个类中,整个库也是围绕这两个类展开。

本文接下来的内容也将围绕这两个类来展开,先建立一个感性的认识,再详细分析它们的使用及原理,最后我们会看一下 Retrofit、OkHttp 是如何使用 Okio 的,以及 Gzip 压缩这个功能是如何设计实现的。

1.1,ByteString

string 这个词本意是“串”,只不过在编程语言的世界中,我们基本都用它来指代“字符串”,其实字符串应该叫 CharString,因此 ByteString 的意义也就很好理解了,“字节串”。

ByteString 代表一个 immutable 字节序列。对于字符数据来说,String 是非常基础的,但在二进制数据的处理中,则没有与之对应的存在,ByteString 应运而生。它为我们提供了对串操作所需要的各种 API,例如子串、判等、查找等,也能把二进制数据编解码为十六进制(hex),base64 和 UTF-8 格式。

它向我们提供了和 String 非常类似的 API:

获取字节:指定位置,或者整个数组;

编解码:hex,base64,UTF-8;

判等,查找,子串等操作;

1.2,Source 和 Sink

在看 Buffer 之前,我们先看一下 Source 和 Sink。

Okio 吸收了 java.io 一个非常优雅的设计:流(stream),流可以一层一层套起来,不断扩充能力,最终完成像加密和压缩这样复杂的操作。再次感谢 Stay 一针见血地指出这正是“修饰模式”的实践。

修饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。

Okio 有自己的流类型,那就是 Source 和 Sink,它们和 InputStream 与 OutputStream 类似,前者为输入流,后者为输出流。

但它们还有一些新特性:

超时机制,所有的流都有超时机制;

API 非常简洁,易于实现;

Source 和 Sink 的 API 非常简洁,为了应对更复杂的需求,Okio 还提供了 BufferedSource 和 BufferedSink 接口,便于使用(按照任意类型进行读写,BufferedSource 还能进行查找和判等);

不再区分字节流和字符流,它们都是数据,可以按照任意类型去读写;

便于测试,Buffer 同时实现了 BufferedSource 和 BufferedSink 接口,便于测试;

Source 和 InputStream 互相操作,我们可以把它们等同对待,同理 Sink 和 OutputStream 也可以等同对待。

1.3,Buffer

我们看一下 Buffer 的类图:

ca394d536cf6ca08e9773450c78f4766.png

这里我们就可以看到,它集 BufferedSource 和 BufferedSink 的功能于一身,为我们提供了访问数据缓冲区所需要的一切 API。

Buffer 是一个可变的字节序列,就像 ArrayList 一样。我们使用时只管从它的头部读取数据,往它的尾部写入数据就行了,而无需考虑容量、大小、位置等其他因素。

和 ByteString 一样,Buffer 的实现也使用了很多高性能的技巧。它内部使用一个双向 Segment 链表来保存数据,Segment 是对一小段字节数组的封装,保存了这个字节数组的一些访问信息,数据的移动通过 Segment 的转让完成,避免了数据拷贝的开销。而且 Okio 还实现了一个 Segment 对象池,以提高我们分配和释放字节数组的效率。

2,ByteString 详解

ByteString 整个类不到 500 行,完全可以通读,但我们还是从实际的使用例子出发。

private static final ByteString PNG_HEADER = ByteString.decodeHex(“89504e470d0a1a0a”);

public void decodePng(InputStream in) throws IOException{

BufferedSource pngSource = Okio.buffer(Okio.source(in));

ByteString header = pngSource.readByteString(PNG_HEADER.size());

if (!header.equals(PNG_HEADER)) {

throw new IOException(“Not a PNG.”);

}

// …

pngSource.close();

}

这里我们可以看到,我们可以直接从十六进制字符串得到它所表示的字节串,我们看看它的内部实现:

public static ByteString decodeHex(String hex){

// …

byte[] result = new byte[hex.length() / 2];

for (int i = 0; i < result.length; i++) {

int d1 = decodeHexDigit(hex.charAt(i * 2)) << 4;

int d2 = decodeHexDigit(hex.charAt(i * 2 + 1));

result[i] = (byte) (d1 + d2);

}

return of(result);

}

private static int decodeHexDigit(char c){

if (c >= ‘0’ && c <= ‘9’) return c – ‘0’;

if (c >= ‘a’ && c <= ‘f’) return c – ‘a’ + 10;

if (c >= ‘A’ && c <= ‘F’) return c – ‘A’ + 10;

throw new IllegalArgumentException(“Unexpected hex digit: ” + c);

}

我们可以看到,它其实就是把每个字符所对应的十六进制值,保存到一个字节数组中,然后利用 of 这个工厂方法构造一个 ByteString 对象。

那我们再看一下它的判等是怎么实现的:

@Override public boolean equals(Object o){

if (o == this) return true;

return o instanceof ByteString

&& ((ByteString) o).size() == data.length

&& ((ByteString) o).rangeEquals(0, data, 0, data.length);

}

public boolean rangeEquals(int offset, byte[] other, int otherOffset, int byteCount){

return offset >= 0 && offset <= data.length – byteCount

&& otherOffset >= 0 && otherOffset <= other.length – byteCount

&& arrayRangeEquals(data, offset, other, otherOffset, byteCount);

}

public static boolean arrayRangeEquals(

byte[] a, int aOffset, byte[] b, int bOffset, int byteCount){

for (int i = 0; i < byteCount; i++) {

if (a[i + aOffset] != b[i + bOffset]) return false;

}

return true;

}

不出所料,果然就是把指定范围内的字节逐个对比!当然就是这样,因为我们对串相等的定义本来就是这样的。

其他的方法这里就不一一展开,不过其中有两点高性能实现技巧值得一提:

把一个 String 编码为 utf8 时,会引用原 String,后面解码时就可以直接返回了

由于 immutable,所以不怕被篡改,所以 toAsciiLowercase,toAsciiUppercase,substring 等函数的实现中,如果需要返回的内容和自身一样,那就会直接返回 this

3,Buffer 详解

我们继续看 PNG 解码的例子:

public void decodePng(InputStream in) throws IOException{

BufferedSource pngSource = Okio.buffer(Okio.source(in));

ByteString header = pngSource.readByteString(PNG_HEADER.size());

if (!header.equals(PNG_HEADER)) {

throw new IOException(“Not a PNG.”);

}

while (true) {

Buffer chunk = new Buffer();

// Each chunk is a length, type, data, and CRC offset.

int length = pngSource.readInt();

String type = pngSource.readUtf8(4);

pngSource.readFully(chunk, length);

int crc = pngSource.readInt();

decodeChunk(type, chunk);

if (type.equals(“IEND”)) break;

}

pngSource.close();

}

我们先看看 Okio.buffer(Okio.source(in)) 做了些什么:

“` java

public static Source source(InputStream in) {

return source(in, new Timeout());

}

private static Source source(final InputStream in, final Timeout timeout) {

// …

return new Source() {

@Override public long read(Buffer sink, long byteCount) throws IOException {

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/138733.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 解决P2P传输瓶颈

    解决P2P传输瓶颈随着嵌入式技术这几年的迅速发展,在个人电脑平台上正显示出强大市场需求的流媒体技术,目前逐渐有向嵌入式平台转移的趋势。个人便携化、家电化的媒体消费需求,为这次平台的转移提供了市场契机。IPTV正是在这场消费革命中处在风口浪尖的焦点。业内大多认为IPTV蕴含了巨大的商业利益,但至今仍鲜有涉足者,其中原因之一是网络速度问题。本文介绍了对等网络技术,即点对点(P2P)技术,将其应用到IPTV网络传输中,可

  • googlenet网络结构图_代码架构

    googlenet网络结构图_代码架构前言七夕了,看着你们秀恩爱,单身狗的我还是做俺该做的事吧!在上一篇文章中介绍了VGG网络结构,VGG在2014年ImageNet中获得了定位任务第1名和分类任务第2名的好成绩,而同年分类任务的第一名则是GoogleNet。GoogleNet是Google研发的深度网络结构,之所以叫“GoogLeNet”,是为了向“LeNet”致敬,有兴趣的同学可以看下原文GoingDeeperwithConvolutions。与VGGNet模型相比较,GoogleNet模型的网络深度已经达到了22层(

  • pycharm将代码同步到远程服务器_pycharm连接python调试器失败

    pycharm将代码同步到远程服务器_pycharm连接python调试器失败pycharm远程调试程序时出现“Couldn’tconnecttoconsoleprocess.Processfinishedwithexitcode-1”针对于错误代码为-1的情况,本人解决方式如下:pycharm→\rightarrow→EditConfigrations→\rightarrow→python→\rightarrow→Runwithp…

  • python数据结构和算法(题目NFA转化DFA算法实现)

    一、什么是DFA算法DFA全称为:DeterministicFiniteAutomaton,即确定有穷自动机。其特征为:有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个符号,其中一个状态是初态,某些状态是终态。但不同于不确定的有限自动机,DFA中不会有从同一状态出发的两条边标志有相同的符号。其实对于DFA算法的定义还是有点抽象,下面的图文并茂或许会对你有帮助!词库的…

  • vmware找不到vmx文件_虚拟机重启后文件丢失

    vmware找不到vmx文件_虚拟机重启后文件丢失在使用Vmware的过程中,不小心删除了vmx文件,导致Vmware无法启动。经过上网搜查资料,找到解决办法。vmx只是一个对Vmware文件的简单描述性文件,并不包含任何实质性信息,信息主要包含在vmdk和vmxf文件中。对于Ubuntu虚拟机,用记事本创建空白文件,在其中输入下面内容并保存为ubuntu.vmx即可。(其中加粗的部分是需要修改的内容,包括vmdk文件的

  • linux复制/剪切文件到另一个文件夹「建议收藏」

    linux复制/剪切文件到另一个文件夹「建议收藏」复制/拷贝:cp文件名路径cphello.csv./python/ml:把当前目录的hello.csv拷贝到当前目的python文件夹里的ml文件夹里cp源文件名新文件名cphello.txtworld.txt:复制并改名,并存放在当前目录下cpfile1file2复制一个文件cpdir/*.复制一个目录下的所有文件…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号