OkHttp的Okio在CacheInterceptor中的应用
目录
Okio的诞生
OKio的简单介绍
缓存模块
超时机制
几个重要的类
简单的读写操作
一个简单的java+socket来实现请求服务器
在CacheInterceptor的运用
1)写请求的头部header
2)写请求体body的数据
3)向服务器发送数据
4)接收服务器的返回的头部header
5)读取服务器返回的response的body数据
总结
Okio的诞生
Okio是用来对数据进行存储和处理IO数据。
InputStream/ OutputStream |
传统的IO,阻塞式IO操作,一直等到有数据才会返回。 |
NIO | 非阻塞式IO。数据从通道(Channel)读取到缓冲区(Buffer),也可以从Buffer读取到Channel中。而Selector允许单线程处理多个通道,用来选出一个可用的通道。 |
Okio | 增加了缓存机制、超时机制来实现快速访问、存储和处理IO数据 |
OKio的简单介绍
缓存模块
由Buffer、Segment、SegmentPool组成。Buffer采用的是有Segment组成的循环链表,来缓存数据;SegmentPool存放的是暂时不用的Segment的单链表,防止频繁进行删除数据操作
超时机制
1)Timeout
在处理InputStream和OutputStream时,传入的超时类,在使用Okio进行读写操作的时候,如果超时就通timeout.throwIfReached()抛出异常。
2)AsyncTimeout
异步超时类,通过一个线程在后台监听Socket是否有超时的操作。
几个重要的类
1)Source/Sink:
接口类。代表输入流和输出流,类似于InputStream/OutputStream,用来进行读写数据;
2)BufferedSource/BufferedSink:
分别继承Source/Sink,扩展了读写功能;
3)RealBufferedSource/RealBufferedSink:
BufferedSource/BufferedSink的实现类,用来完成数据的读写操作。查看里面的源码中可以发现,其实里面就是含有一个Source/Sink对象的代理和一个Buffer对象,真正的去完成读写操作的就是这个Buffer。
简单的读写操作
在使用Okio在进行读写文件的时候,首先要将文件转换成一个Source/Sink,然后在换成BufferedSource/BufferedSink,最后通过API直接读写数据
//获取文件
File file = new File(fileName);
//将file转换成Source
Source source = Okio.source(file);
//将source转换成BufferedSource
BufferedSource buff = Okio.buffer(source);
//读取BufferedSource里面的内容
String result = buff.readString(Charset.forName("utf-8"));
一个简单的java+socket来实现请求服务器
在用java的Socket来进行实现请求服务器的过程,向服务器发送数据就是从socket去取出OutputStream,然后将请求数据写入OutputStream;同样读取服务器返回的数据时,就是从socket中取出InputStream,然后从InputStream数据中读取即可。下面是简单的代码实例
1)向服务器发送数据
//向服务器发送数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write("写数据".getBytes("UTF-8"));
outputStream.close();
2)接收服务器返回的数据
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//然后就等着服务器发送过数据之后,读取服务器发来的信息String line = null;StringBuffer buffer = new StringBuffer();
while ((line = bufferedReader.readLine()) != null) {buffer.append(line);
}
在CacheInterceptor的运用
1)写请求的头部header
httpCodec.writeRequestHeaders(request);
这个httpCodec就是Http1Codec或者Http2Codec的实例。为了方便描述,我们拿Http1Codec举例说明。进入到Http1Codec源码中可以看到,最终调用的是下面的这个方法:
public void writeRequest(Headers headers, String requestLine) throws IOException {if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);sink.writeUtf8(requestLine).writeUtf8("\r\n");for (int i = 0, size = headers.size(); i < size; i++) {sink.writeUtf8(headers.name(i)).writeUtf8(": ").writeUtf8(headers.value(i)).writeUtf8("\r\n");}sink.writeUtf8("\r\n");state = STATE_OPEN_REQUEST_BODY;}
其实就是往sink里面写入了请求头header的数据。 而这个sink是在RealConnection中创建链接通道的时,在实例化Http1Codec的时候,从RealConnection中传入的,而sink的实例化也在RealConnection中。
private void connectSocket(int connectTimeout, int readTimeout, Call call,EventListener eventListener) throws IOException {
//...代码省略source = Okio.buffer(Okio.source(rawSocket));sink = Okio.buffer(Okio.sink(rawSocket));
//...代码省略
}
进入到Okio中的sink()方法中,可以看到
public static Sink sink(Socket socket) throws IOException {//.....代码省略Sink sink = sink(socket.getOutputStream(), timeout);return timeout.sink(sink);}
就是从socket中获取输出流进行转换成Sink,在经过buffer(),最终返回的是RealBufferedSink
public static BufferedSink buffer(Sink sink) {return new RealBufferedSink(sink);}
所以我们在Http1Codec中的sink其实就是RealBufferedSink的对象实例。我们往sink中写入信息,其实就是写到了Buffer的缓存中。
2)写请求体body的数据
CountingSink requestBodyOut =new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
和写header的方式一样,先通过httpCodec.createRequestBody()来获取到请求body的数据,然后将数据转换成Sink,再将sink转换成BufferedSink,然后就是调用request().body().writeTo()方法进行将请求体body写入Buffer缓存中。看下request().body()返回的值body该对象就是在通过建造者模式创建Request的时候,在build()方法
Request build() {
//.....代码省略RequestBody body = this.body;if (body == null) {// Try to pull from one of the builders.if (formBuilder != null) {body = formBuilder.build();} else if (multipartBuilder != null) {body = multipartBuilder.build();} else if (hasBody) {// Body is absent, make an empty body.body = RequestBody.create(null, new byte[0]);}}
//.....代码省略}
假设返回的是MultipartBody,那么最终调用的就是MultipartBody里面的writeTo()方法,进入到源码中可以看到也就是调用sink.write()来将body写入到Buffer的缓存中。
3)向服务器发送数据
httpCodec.finishRequest();
真正的将数据发送给服务器。进入到Http1Codec看下finishRequest()源码:
@Override public void finishRequest() throws IOException {sink.flush();}
调用的是RealBufferedSink的flush(),进入到RealBufferedSink中查看源码
@Override public void flush() throws IOException {if (closed) throw new IllegalStateException("closed");if (buffer.size > 0) {sink.write(buffer, buffer.size);}sink.flush();}
最终调用的是的就是RealBufferedSink的write()
@Override public void write(Buffer source, long byteCount)throws IOException {if (closed) throw new IllegalStateException("closed");buffer.write(source, byteCount);emitCompleteSegments();}
将请求信息写入到OutputStream中,完成将数据发送给服务器。
4)接收服务器的返回的头部header
if (responseBuilder == null) {realChain.eventListener().responseHeadersStart(realChain.call());responseBuilder = httpCodec.readResponseHeaders(false);}
进入Http1Codec源码中查看
@Override public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {//......省略代码try {StatusLine statusLine = StatusLine.parse(readHeaderLine());Response.Builder responseBuilder = new Response.Builder().protocol(statusLine.protocol).code(statusLine.code).message(statusLine.message).headers(readHeaders());//......省略代码}
通过readHeaderLine()将服务器返回的数据转换成StatusLine。其中通过source将返回的数据读出,而source的传入同1)中提到的sink的方式一样,都是在RealConnection中实例化时传入的。
private String readHeaderLine() throws IOException {String line = source.readUtf8LineStrict(headerLimit);headerLimit -= line.length();return line;}
查看Okio中的Okio.source()的源码可以发现
public static Source source(Socket socket) throws IOException {
//......省略代码AsyncTimeout timeout = timeout(socket);Source source = source(socket.getInputStream(), timeout);return timeout.source(source);}
该source()就是从socket的inputStream中读取数据。
5)读取服务器返回的response的body数据
response = response.newBuilder().body(httpCodec.openResponseBody(response)).build();
进入到源码中发现,在Http1Codec中的openResponseBody(),就是根据不同的条件返回RealResponseBody的对象。
return new RealResponseBody(contentType, contentLength, Okio.buffer(source));
总结
Okio提供了阻塞IO和非阻塞IO的功能,同时增加了缓存和超时机制。在HttpCodec中通过Okio来实现读写服务器数据。
OkHttp的Okio在CacheInterceptor中的应用相关推荐
- android okhttp3 okio,OkHttp和Okio
OkHttp和Okio 文本将介绍OkHttp和Okio基本使用 OkHttp HTTP 是现在APP访问网络最流行的方式.通过它我们可以交换数据和媒体信息.而高效的使用HTTP可以让你的加载数据更快 ...
- OkHttp –Android、Java应用中的 HTTP SPDY 客户端库
原文链接: http://blog.chengyunfeng.com/?p=489 我们通过HTTP在设备和服务器之前交换数据.高效的使用HTTP可以让您的应用运行更快.更节省流量.而OkHttp库就 ...
- Okhttp 之 okio
本文是的前一篇文章 Okhttp IO 之 Segment & SegmentPool 的基础上写的,如果你没看懂前面的文章,那么看本文会相当的吃力,因为很多关键的代码都是在前面这篇文章中剖析 ...
- android中okhttp原理详解,Android中okhttp原理详解-极度针对面试篇
一.okhttp工做的大体流程 1.一.总体流程 (1).当咱们经过OkhttpClient创立一个Call,并发起同步或者异步请求时: (2).okhttp会经过Dispatcher对咱们全部的Re ...
- OKHttp之OkIO
OKIO的核心:Sink和Souce okio的本质是对InputStream和OutputStream做了进一步封装 内部通过Segment组成的双向链表来持有数据
- Android/Java中okhttp用法介绍
最近Android老项目改造, 之前老代码一直用AndroidHttpClient实现网络请求,存在以下问题 1. 版本太老旧, 最近无更新; 2. 网络请求模块代码太散乱,不好管理, 3. 不支持 ...
- java中的IO、NIO、Okio
java IO 写 这种写方法只能一个字节一个字节的写: 注意把要关闭的流写在try括号中,省去了代码中finally关闭的过程,以下例子均是. private static void ioWrite ...
- Android中的OKHttp请求网络
OKHttp 文章目录 OKHttp 一.简介 二.基本用法 1.依赖 2.使用OKHttp 2.1.get请求 2.2.post请求 3.案例操作演示(借助runOnUiThread()方法进行线程 ...
- Android—OkHttp同步异步请求过程源码分析与拦截器
OkHttp同步请求步骤: 创建OkHttpClient,客户对象 创建Request,请求主体,在请求主体设置请求的url,超时时间等 用newCall(request)将Reuqest对象封装成C ...
最新文章
- String、StringBuffer与StringBuilder之间区别 (转载)
- 如何使用Spring优雅地处理REST异常?
- Drawable 详解
- LeetCode Algorithm 22. 括号生成
- 引发类型为“System.Windows.Forms.AxHost+InvalidActiveXStateException”的异常 解决
- MATLAB安装机器人学工具箱
- setnx是原子操作吗_Redis面试七连问,你能扛得住吗?
- react改变checkbox的文字类型_React Checkbox不发送onChange
- python输入路径读取文件-python获取程序执行文件路径的方法(推荐)
- 【图论】拓扑排序:一个名字高大上的实际很简单的算法(图文详解)
- PHP5.6中php-fpm的配置、启动、关闭和重启
- AI的委屈只有它知道……
- C++面向对象课程设计实例-图书馆借阅系统
- 做一个微信欢乐斗地主之残局解答器!
- Virtual-Taobao: Virtualizing Real-World Online Retail Environment for Reinforcement Learning
- 2020年鼠年正月二十一 雪中送炭难
- photoshop怎么设计淘宝天猫海报amp;nb…
- win10系统升级一段时间后,内存占用过高
- MiniFly微型四轴开发学习日志(一)——MiniFly 微型四轴软件原理
- python pandas 在现有excel中插入新数据