android okio使用方法,Android 开源框架 Okio 原理剖析
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 的类图:
这里我们就可以看到,它集 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 {
android okio使用方法,Android 开源框架 Okio 原理剖析相关推荐
- 黄聪:Android酷炫实用的开源框架(UI框架)(转)
Android酷炫实用的开源框架(UI框架) 前言 忙碌的工作终于可以停息一段时间了,最近突然有一个想法,就是自己写一个app,所以找了一些合适开源控件,这样更加省时,再此分享给大家,希望能对大家有帮 ...
- ym——Android酷炫实用的开源框架(UI框架)(终)
转载请注明本文出自Cym的博客(http://blog.csdn.net/cym492224103),谢谢支持! 前言 好久没写博文了,最近工作比较忙,剩下的一点点时间在做自己的项目,在Android ...
- Android 学习笔记之Volley开源框架解析(一)
PS:看完了LGD的六场比赛...让人心酸... 学习内容: 1.Http请求的过程... 2.Volley的简单介绍... 1.Http请求... 这里只是简单的说一下Http请求的过程.. ...
- android 最新开源框架,Android酷炫实用的开源框架(UI框架)
前言 忙碌的工作终于可以停息一段时间了,最近突然有一个想法,就是自己写一个app,所以找了一些合适开源控件,这样更加省时,再此分享给大家,希望能对大家有帮助,此博文介绍的都是UI上面的框架,接下来会有 ...
- Android酷炫实用的开源框架(UI框架)
前言 忙碌的工作终于可以停息一段时间了,最近突然有一个想法,就是自己写一个app,所以找了一些合适开源控件,这样更加省时,再此分享给大家,希望能对大家有帮助,此博文介绍的都是UI上面的框架,接下来会有 ...
- Android免Root环境下Hook框架Legend原理分析
0x1 应用场景 现如今,免Root环境下的逆向分析已经成为一种潮流! 在2015年之前的iOS软件逆向工程领域,要想对iOS平台上的软件进行逆向工程分析,越狱iOS设备与安装Cydia是必须的!几乎 ...
- android datepicker使用方法,android DatePicker
Date Picker 提供了一个构件去选择一个时间,使用DatePicker构件,该构件允许用户去选择月,日,年在一个界面中. 实例介绍 首先我们应该先创建一个DatePickerDialog,该D ...
- android view使用方法,Android View构造方法第三参数使用方法详解
我们都知道,在Android中要使用一个View,一般会有两种方式: 在XML文件中配置: 直接在代码中new一个View的对象. 我们今天讨论的内容就是围绕着View的构造方法的. 1.实例 首先我 ...
- android返回键方法,Android按返回键(后退键)Back键事件捕获的两种方法
package zhangphil.back; import android.support.v7.app.ActionBarActivity; import android.util.Log; im ...
最新文章
- webflow如何使用_我如何使用Webflow构建辅助项目以帮助设计人员进行连接
- 2.18比赛(T2,T3留坑)
- Tomcat类加载器为何违背双亲委派模型
- Java二进制文件示例
- MYSQL Innodb逻辑存储结构
- 【官方方法】ROS源
- slideToggle使用
- C++线程安全单例类最全总结
- 学习总结5.0 Linux tar打包命令
- 信息安全实验三 :PGP邮件加密软件的使用
- 个人独资有限公司章程模板
- SQL Server 2008 R2安装步骤示例
- IMDB排名前100名经典电影
- 第3关:球的表面积和体积
- win11怎么看电脑显卡信息
- 麻雀优化算法 优化XGBoost的参数 python代码
- 股票量化投资策略有哪些特点?
- C#图解教程(第三章)
- 全网最全音视频媒体流
- 雨花石能不能养人带辐射吗?