前两章把一些重点讲了一下,这一章把剩余的都过一遍。前面两章中,可以看到 Okio 中用到了大量的委托模式,知道了这点,再看剩余的几个类就好理解的多了。
首先看 ForwardingSink 和 ForwardingSource 这两个类,他们是抽象类,但里面没有抽象方法,并且里面的操作全程都是代理模式
public abstract class ForwardingSink implements Sink {
private final Sink delegate;
public ForwardingSink(Sink delegate) {
if (delegate == null) throw new IllegalArgumentException("delegate == null");
this.delegate = delegate;
}
...
}
public abstract class ForwardingSource implements Source {
private final Source delegate;
public ForwardingSource(Source delegate) {
if (delegate == null) throw new IllegalArgumentException("delegate == null");
this.delegate = delegate;
}
...
}
这说明什么呢?说明他们不能直接使用,需要有子类来继承他们。为什么这么设计呢?比如说我们复制一个文件,想用进度条显示进度,怎么办? RealBufferedSink 中能获取到写入的文件的长度吗?这时候就需要用到 ForwardingSink 来定义子类了
public class ProgressSink extends ForwardingSink {
private long bytesWritten = 0;
private Listen listen;
private long totalLength;
public ProgressSink(Sink delegate, long length, Listen listen) {
super(delegate);
this.listen = listen;
totalLength = length;
}
@Override
public void write(Buffer source, long byteCount) throws IOException {
super.write(source, byteCount);
bytesWritten += byteCount;
listen.onProgress(bytesWritten, totalLength);
}
public interface Listen{
void onProgress(long bytesWritten, long contentLength);
}
}
定义个回调,把要复制文件的长度通过构造传进来,然后重写 write() 方法,在它的基础上计算当前复制的长度,然后通过回调把数据传出去。
用法如下,创建原始的 Sink 传入 ProgressSink 中,然后把它传入 Okio.buffer() 中即可
private static void copeListener(File file, File fileDst) {
try {
long length = file.length();
Sink sink= Okio.sink(fileDst);
ProgressSink countingSink = new ProgressSink(sink, length, listen);
BufferedSink bufferedSink = Okio.buffer(countingSink);
Source source = Okio.source(file);
bufferedSink.writeAll(source);
bufferedSink.flush();
source.close();
bufferedSink.close();
} catch (Exception e) {
e.printStackTrace();
}
}
static ProgressSink.Listen listen = new ProgressSink.Listen() {
@Override
public void onProgress(long bytesWritten, long total) {
float pos = bytesWritten * 100.0f / total;
System.out.println(" progress " + pos);
}
};
这样复制的时候,就把进度读了出来。如果我们想写一个读取文本的进度条,可以仿照上面的写法,继承 ForwardingSource 来写一个试试。HashingSink 和 HashingSource 用的也是委托模式,里面对外提供了 md5、HA-1 及 SHA-256 三种默认格式,我们也可以自定义格式。
GzipSink 和 GzipSource 是压缩流,它们内部用到了 DeflaterSink 和 InflaterSource 这两个类。压缩流的好处就是能极大的压缩体积,比如说我想往一个文本中写入数据,直接上例子
private static void zipCompress() {
String ss = "中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" + "中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" + "中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" + "中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" + "中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +
"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" + "中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" + "中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" + "中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿中国好男儿" +"";
File file = new File("E:", "abcd.zip");
try {
BufferedSink gzipSink = Okio.buffer(new GzipSink(Okio.sink(file)));
gzipSink.writeUtf8(ss);
gzipSink.flush();
gzipSink.close();
GzipSource gzipSource=new GzipSource(Okio.source(file));
BufferedSource bufferedSource = Okio.buffer(gzipSource);
String content = bufferedSource.readUtf8();
gzipSource.close();
System.out.println(content);
} catch (Exception e) {
e.printStackTrace();
}
}
我们往文本中写了大量的内容,并且最下面通过压缩流把它读了出来,打印的值与上面写入的值能对应上;重点是我们生成的 abcd.zip 文件大小仅1kb,如果是以普通流的方式写入,大小时7kb(上面文件名字是 abcd.zip ,换成 abcd.txt 也是一样的)。我们通过 OkHttp 中上传压缩数据时,就可以用到 GzipSink 来压缩数据了,我们用 Post 请求,我们创建一个 RequestBody 对象
private void uploadZipFile(String url, final String jsonString) throws IOException {
RequestBody requestBody = new RequestBody() {
@Override
public MediaType contentType() {
return MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8");
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
BufferedSink gzipSink = Okio.buffer(new GzipSink(Okio.sink(sink.outputStream())));
gzipSink.write(jsonString.getBytes());
gzipSink.flush();
gzipSink.close();
}
};
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new GzipInterceptor())//开启Gzip压缩
.build();
okhttp3.Response response = okHttpClient.newCall(request).execute();
}
static class GzipInterceptor implements Interceptor {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.header("Content-Encoding","gzip")
.build();
return chain.proceed(request);
}
}
上面仅仅是个例子,我们平常如果上报大量数据时,可以使用压缩上报,提高效率。
Pipe 是个管道流,这个接触比较少,可以上网搜一下相关资料,它不需要从外面传入 Sink 和 Source,使用内部类,PipeSource 读取数据,发现 buffer.size长度为0的时候, PipeSource 流会等待,PipeSink 流往 Buffer中再写入数据时,阻塞才会消失。
ByteString 类似 String,可以看做是它的兄弟,它对外提供了 base64、md5、sha1、sha256 等格式,
public class ByteString implements Serializable, Comparable<ByteString> {
public static final ByteString EMPTY = ByteString.of();
final byte[] data;
transient int hashCode; // Lazily computed; 0 if unknown.
transient String utf8; // Lazily computed.
public String utf8() {
String result = utf8;
return result != null ? result : (utf8 = new String(data, Util.UTF_8));
}
...
}
它的代码比较简单,用 data 数组记录数据,然后用 utf8 来记录 UTF-8 格式的字符串。总体来说,Okio 写的还是比较厉害的,在了解 IO 流的基础上,进行封装优化。