博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Okio简化处理I/O操作原理
阅读量:7035 次
发布时间:2019-06-28

本文共 13369 字,大约阅读时间需要 44 分钟。

转载:http://blog.csdn.net/sbsujjbcy/article/details/50523623

Okio库是一个由square公司开发的,它补充了java.io和java.nio的不足,以便可以更加方便。高速的訪问、存储和处理你的数据。

而OkHttp的底层也使用该库作为支持。而在开发中,使用该库可以大大给你带来方便。

眼下,Okio的最新版本号是1.6.0,gradle的引用例如以下

compile 'com.squareup.okio:okio:1.6.0'
  • 1
  • 2
  • 3

Okio中有两个关键的接口,Sink和Source,这两个接口都继承了Closeable接口;而Sink能够简单的看做OutputStream,Source能够简单的看做InputStream。

而这两个接口都是支持读写超时设置的。

结构图例如以下

这里写图片描写叙述

它们各自有一个支持缓冲区的子类接口,BufferedSink和BufferedSource。而BufferedSink有一个实现类RealBufferedSink,BufferedSource有一个实现类RealBufferedSource。此外。Sink和Source它门还各自有一个支持gzip压缩的实现类GzipSink和GzipSource;一个具有托付功能的抽象类ForwardingSink和ForwardingSource;另一个实现类便是InflaterSource和DeflaterSink,这两个类主要用于压缩。为GzipSink和GzipSource服务;总体的结构图例如以下

这里写图片描写叙述

BufferedSink中定义了一系列写入缓存区的方法,比方write方法写byte数组,writeUtf8写字符串,另一些列的writeByte,writeString。writeShort,writeInt。writeLong。writeDecimalLong等等方法;

BufferedSource定义的方法和BufferedSink极为相似。仅仅只是一个是写一个是读,基本上都是一一相应的,如readUtf8。readByte,readString,readShort。readInt等等等等。这两个接口中的方法有兴趣的点源代码进去看就能够了。

而这两个支持缓冲区的接口的实现类RealBufferedSink和RealBufferedSource都是通过包装一个Sink+Buffer或者Source+Buffer来进行实现的。

例如以下图所看到的

这里写图片描写叙述

拿RealBufferedSink来举例,实际调用的write的一系列方法。都是直接的对成员变量buffer进行的操作,当写入buffer成功后,最后会调用一个方法将buffer中的内容写入到sink中去,我们随便拿一个方法看一下

    public BufferedSink writeLong(long v) throws IOException {        if(this.closed) {            throw new IllegalStateException("closed");        } else {            this.buffer.writeLong(v);            return this.emitCompleteSegments();        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

能够看到,首先会推断closed成员变量是否是标记着关闭。假设已经关闭了则扔出一个异常。否则将内容写入到buffer。写入完毕后调用了一个emitCompleteSegments的方法。该方法中做了什么呢,没错。就是将buffer中的内容写入到sink成员变量中去。然后将自身返回。

    public BufferedSink emitCompleteSegments() throws IOException {        if(this.closed) {            throw new IllegalStateException("closed");        } else {            long byteCount = this.buffer.completeSegmentByteCount();            if(byteCount > 0L) {                this.sink.write(this.buffer, byteCount);            }            return this;        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

这两个实现类的内部的全部方法都是类似的。这里不一一展开。

而这一切的背后都是一个叫做Buffer的类在支持着缓冲区,Buffer是BufferedSink和BufferedSource的实现类,因此它既能够用来读数据,也能够用来写数据,其内部使用了一个Segment和SegmentPool。维持着一个链表,其循环利用的机制和Android中Message的利用机制是一模一样的。

final class SegmentPool {    static final long MAX_SIZE = 65536L;    static Segment next;    static long byteCount;    private SegmentPool() {    }    static Segment take() {        Class var0 = SegmentPool.class;        synchronized(SegmentPool.class) {            if(next != null) {                Segment result = next;                next = result.next;                result.next = null;                byteCount -= 2048L;                return result;            }        }        return new Segment();    }    static void recycle(Segment segment) {        if(segment.next == null && segment.prev == null) {            if(!segment.shared) {                Class var1 = SegmentPool.class;                synchronized(SegmentPool.class) {                    if(byteCount + 2048L <= 65536L) {                        byteCount += 2048L;                        segment.next = next;                        segment.pos = segment.limit = 0;                        next = segment;                    }                }            }        } else {            throw new IllegalArgumentException();        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83

内部一个成员变量next指向链表下一个元素,take方法首先推断池中是否存在可用的,存在则返回,不存在则new一个,而recycle则是将不再使用的Segment又一次扔到池中去。从而达到一个Segment池的作用。

而Okio暴露给外部使用的类便是Okio这个类。其内部有大量的静态方法,包含通过一个Source获得BufferedSource。通过一个Sink获得一个BufferedSink。这个过程非常easy,我们调用Okio的buffer方法就可以返回我们须要的,例如以下

Okio.buffer(sink)Okio.buffer(source)
  • 1
  • 2
  • 3
  • 4
  • 5

可是上面两个方法须要传递一个Sink或者Source。那么这个Sink和Source又是怎样获得的呢。事实上方法也在Okio这个类中。我们能够调用sink方法获得一个Sink,调用source方法获得一个Source,而数据的来源或者目的能够是一个File,一个输入或者输出流。一个Socket链接等等。例如以下

Okio.sink(new File("***"));Okio.sink(new FileOutputStream(new File("***")));Okio.sink(new Socket("***",8888));Okio.source(new File("***"));Okio.source(new FileInputStream(new File("***")));Okio.source(new Socket("****",8888));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这样你可能还只是瘾。那么让我们连起来应用一下,如今我们从本地读一个文件,读完后再往还有一个文件里写入内容。

public static void main(String[] args) {    Source source = null;    BufferedSource bufferedSource = null;    try {        File file = new File("resources/test.txt");        source = Okio.source(file);        bufferedSource = Okio.buffer(source);        String content = bufferedSource.readUtf8();        System.out.println(content);    } catch (FileNotFoundException e) {        e.printStackTrace();    } catch (IOException e) {        e.printStackTrace();    } finally {        closeQuietly(bufferedSource);    }    Sink sink = null;    BufferedSink bufferedSink = null;    try {        File dest = new File("resources/dest.txt");        sink = Okio.sink(dest);        bufferedSink = Okio.buffer(sink);        bufferedSink.writeUtf8("11111111111");    } catch (FileNotFoundException e) {        e.printStackTrace();    } catch (IOException e) {        e.printStackTrace();    } finally {        closeQuietly(bufferedSink);    }}public static void closeQuietly(Closeable closeable) {    if (closeable != null) {        try {            closeable.close();        } catch (RuntimeException rethrown) {            throw rethrown;        } catch (Exception ignored) {        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91

也许有时候网络请求中,我们须要使用到Gzip的功能。那么。我们能够简单的使用一下gzip的功能

public static void main(String[] args) {    Sink sink = null;    BufferedSink bufferedSink = null;    GzipSink gzipSink=null;    try {        File dest = new File("resources/gzip.txt");        sink = Okio.sink(dest);        gzipSink=new GzipSink(sink);        bufferedSink = Okio.buffer(gzipSink);        bufferedSink.writeUtf8("android vs ios");    } catch (FileNotFoundException e) {        e.printStackTrace();    } catch (IOException e) {        e.printStackTrace();    } finally {        closeQuietly(bufferedSink);    }    Source source = null;    BufferedSource bufferedSource = null;    GzipSource gzipSource=null;    try {        File file = new File("resources/gzip.txt");        source = Okio.source(file);        gzipSource=new GzipSource(source);        bufferedSource = Okio.buffer(gzipSource);        String content = bufferedSource.readUtf8();        System.out.println(content);    } catch (FileNotFoundException e) {        e.printStackTrace();    } catch (IOException e) {        e.printStackTrace();    } finally {        closeQuietly(bufferedSource);    }}public static void closeQuietly(Closeable closeable) {    if (closeable != null) {        try {            closeable.close();        } catch (RuntimeException rethrown) {            throw rethrown;        } catch (Exception ignored) {        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

验证是否正确的方法便是查看该写入的文件是否是乱码,以及读出来是否是原来的字符串。

对照一下原来的gzip压缩与解压缩的方式。你就会发现还是简单了不少的

public class GzipUtil {    /**     * GZIP压缩     *     * @param data     * @return     */    public static byte[] gzip(byte[] data) throws Exception {        if (data == null || data.length == 0) {            return null;        }        ByteArrayOutputStream out = new ByteArrayOutputStream();        GZIPOutputStream zos;        BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(data));        byte[] buf = new byte[512];        int len;        try {            zos = new GZIPOutputStream(out);            while ((len = bis.read(buf)) != -1) {                zos.write(buf, 0, len);                zos.flush();            }            bis.close();            zos.close();            return out.toByteArray();        } finally {            if (out != null) {                try {                    out.close();                } catch (Exception e2) {                }            }        }    }    /**     * Gzip解压缩     * @param b     * @return     */    public static byte[] unGzip(byte[] b) {        if (b == null || b.length == 0) {            return null;        }        ByteArrayOutputStream out = new ByteArrayOutputStream();        ByteArrayInputStream in = new ByteArrayInputStream(b);        try {            GZIPInputStream gunzip = new GZIPInputStream(in);            byte[] buffer = new byte[256];            int n;            while ((n = gunzip.read(buffer)) >= 0) {                out.write(buffer, 0, n);            }            return out.toByteArray();        } catch (IOException e) {            Log.e(WDCore.getInstance().getConfiguration().getLogTag(), "uncompress error", e);        } finally {            try {                if (out != null) {                    out.close();                }                if (in != null) {                    in.close();                }            } catch (Exception e2) {            }        }        return null;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171

此外另一个ByteString类,这个类能够用来做各种变化。它将byte转会为String,而这个String能够是utf8的值。也能够是base64后的值,也能够是md5的值。也能够是sha256的值,总之就是各种变化,最后取得你想要的值。

总之,在合适的地方适当使用一下Okio这个库。一定能给你开发带来诸多便利,何乐而不为呢。

你可能感兴趣的文章
leetcode371: Sum of 2 Integers
查看>>
css盒子模型
查看>>
python (10) 文件夹的创建与文件夹的删除
查看>>
hdoj 1728 逃离迷宫
查看>>
c# GridView有关RowClick事件,可单击显示选中的row
查看>>
自定义时间选择器调用时报错
查看>>
EMR问题
查看>>
python——迭代器和生成器
查看>>
Func<T, TResult> Delegate
查看>>
paper122:多尺度与多分辨率的关系
查看>>
set clipboard
查看>>
python algorithm
查看>>
第二章 怎样培养好习惯
查看>>
Python错误和异常概念(总)
查看>>
Appnium学习日记三(安装Android模拟器)
查看>>
模式识别之双目立体视觉---双目立体视觉相关介绍
查看>>
开源项目之树莓派---无线监控摄像头
查看>>
大公司视频转码
查看>>
消息队列非阻塞
查看>>
awk编程的基本用法
查看>>