public void readLines(File file) throws IOException {

try (BufferedSource bufferedSource = Okio.buffer(Okio.source(file))) {

for (String line; (line = bufferedSource.readUtf8Line()) != null; ) {

System.out.println(line);

}

}

}

try-with-source是jdk1.7开始提供的语法糖,在try语句()里面的资源对象,jdk最终会自动调用它的close方法去关闭它, 即便try里有多个资源对象也是可以的,这样就不用你手动去关闭资源了。但是在android里面使用的话,会提示你要求API level最低为19才可以。

readUtf8Line()方法适用于大多数文件。对于某些用例,还可以考虑使用readUtf8LineStrict()。类似readUtf8Line(),但它要求每一行都以\n\r\n结尾。如果在这之前遇到文件结尾,它将抛出一个EOFException。它还允许设置一个字节限制来防止错误的输入。

public void readLines(File file) throws IOException {

try (BufferedSource source = Okio.buffer(Okio.source(file))) {

while (!source.exhausted()) {

String line = source.readUtf8LineStrict(1024L);

System.out.println(line);

}

}

}

写文本文件

public void writeEnv(File file) throws IOException {

Sink fileSink = Okio.sink(file);

BufferedSink bufferedSink = Okio.buffer(fileSink);

for (Map.Entry<String, String> entry : System.getenv().entrySet()) {

bufferedSink.writeUtf8(entry.getKey());

bufferedSink.writeUtf8("=");

bufferedSink.writeUtf8(entry.getValue());

bufferedSink.writeUtf8("\n");

}

bufferedSink.close();

}

类似于读文件使用SourceBufferedSource, 写文件的话,则是使用的SinkBufferedSink,同样的在BufferedSink接口中也提供了丰富的接口方法:

其中Okio.buffer(fileSink)内部返回的实现对象是一个RealBufferedSink类的对象, 跟RealBufferedSource一样它也是一个装饰者对象,具备Buffer缓冲功能。同样,以上代码可以使用jdk的try-with-source语法获得更加简便的写法:

public void writeEnv(File file) throws IOException {

try (BufferedSink sink = Okio.buffer(Okio.sink(file))) {

sink.writeUtf8(“啊啊啊”)

.writeUtf8("=")

.writeUtf8(“aaa”)

.writeUtf8("\n");

}

}

其中的换行符\n,Okio没有提供单独的api方法,而是要你手动写,因为这个跟操作系统有关,不过你可以使用System.lineSeparator()来代替\n,这个方法在Windows上返回的是"\r\n"在UNIX上返回的是"\n"。

在上面的代码中,对writeUtf8()进行了四次调用, 这样要比下面的代码更高效,因为虚拟机不必对临时字符串进行创建和垃圾回收。

sink.writeUtf8(entry.getKey() + “=” + entry.getValue() + “\n”);

Gzip压缩和读取

//zip压缩

GzipSink gzipSink = new GzipSink(Okio.sink(file));

BufferedSink bufferedSink = Okio.buffer(gzipSink);

bufferedSink.writeUtf8(“this is zip file”);

bufferedSink.flush();

bufferedSink.close();

//读取zip

GzipSource gzipSource = new GzipSource(Okio.source(file));

BufferedSource bufferedSource = Okio.buffer(gzipSource);

String s = bufferedSource.readUtf8();

bufferedSource.close();

UTF-8

在上面的代码中,可以看到使用的基本都是带UTF-8的读写方法。Okio推荐优先使用UTF-8的方法,why UTF-8? 这是因为UTF-8在世界各地都已标准化,而在早期的计算机系统中有许多不兼容的字符编码如:ISO-8859-1、ShiftJIS、 ASCII、EBCDIC等,使用UTF-8可以避免这些问题。

如果你需要使用其他编码的字符集,可以使用readString()writeString(),这两个方法可以指定字符编码参数,但在大多数情况下应该只使用带UTF-8的方法。

在编码字符串时,需要特别注意字符串的表达形式和编码方式。当字形有重音或其他装饰时,情况可能会有点复杂。尽管在I/O中读写字符串时使用的都是UTF-8,但是当在内存中,Java字符串使用的是已过时的UTF-16进行编码的。这是一种糟糕的编码格式,因为它对大多数字符使用 16-bit char,但有些字符不适合。特别是大多数的表情符号使用的是两个Java字符, 这时就会出现一个问题: String.length()返回的结果是utf-16字符的数量,而不是字体原本的字符数量。

| | Café ? | Café ? |

| --: | :-- | :-- |

| Form | NFC | NFD |

| Code Points | c a f é ␣ ? | c a f e ´ ␣ ? |

| UTF-8 bytes | 43 61 66 c3a9 20 f09f8da9 | 43 61 66 65 cc81 20 f09f8da9 |

| String.codePointCount | 6 | 7 |

| String.length | 7 | 8 |

| Utf8.size | 10 | 11 |

在大多数情况下,Okio无需你关心这些问题,从而可以将关注点放在数据本身的使用上。但是当你确实需要处理这些低级的UTF-8字符串问题时,也有一些方便的API来处理,如使用utf8.size()可以计算字符串按钮UTF-8形式编码的字节数(但是并不会实际编码),使用bufferedsource.readutf8codepoint()读取单个可变长度的Code Point,使用bufferedsink.writeutf8codepoint()写入一个Code Point

public final class ExploreCharsets {

public void run() throws Exception {

dumpStringData(“Café \uD83C\uDF69”); // NFC: é is one code point.

dumpStringData(“Café \uD83C\uDF69”); // NFD: e is one code point, its accent is another.

}

public void dumpStringData(String s) throws IOException {

System.out.println(" " + s);

System.out.println(" String.length: " + s.length());

System.out.println("String.codePointCount: " + s.codePointCount(0, s.length()));

System.out.println(" Utf8.size: " + Utf8.size(s));

System.out.println(" UTF-8 bytes: " + ByteString.encodeUtf8(s).hex());

readUtf8CodePoint(s);

System.out.println();

}

private void readUtf8CodePoint(String s) throws IOException {

Buffer buffer = new Buffer();

buffer.writeString(s, Charset.forName(“utf-8”));

Source source = Okio.source(buffer.inputStream());

BufferedSource bufferedSource = Okio.buffer(source);

int i = -1;

StringBuilder sb = new StringBuilder();

while (!bufferedSource.exhausted() && (i = bufferedSource.readUtf8CodePoint()) != -1) {

sb.append((char) i).append("—");

}

System.out.println(" readUtf8CodePoint: " + sb.toString());

bufferedSource.close();

}

public static void main(String… args) throws Exception {

new ExploreCharsets().run();

}

}

序列化和反序列化

将一个对象进行序列化并以ByteString的形式返回:

private ByteString serialize(Object o) throws IOException {

Buffer buffer = new Buffer();

try (ObjectOutputStream objectOut = new ObjectOutputStream(buffer.outputStream())) {

objectOut.writeObject(o);

}

return buffer.readByteString();

}

这里使用Buffer对象代替java的ByterrayOutputstream,然后从buffer中获得输出流对象,并通过ObjectOutputStream(来自java的Api)写入对象到buffer缓冲区当中,当你向Buffer中写数据时,总是会写到缓冲区的末尾。

最后,通过buffer对象的readByteString()从缓冲区读取一个ByteString对象,这会从缓冲区的头部开始读取,readByteString()方法可以指定要读取的字节数,如果不指定,则读取全部内容。

我们利用上面的方法将一个对象进行序列化,并得到的ByteString对象按照base64格式进行输出:

Point point = new Point(8.0, 15.0);

ByteString pointBytes = serialize(point);

System.out.println(pointBytes.base64());

这样我们会得到输出的一串字符串:

rO0ABXNyADVjb20uZXhhbXBsZS5hc3VzLm15YXBwbGljYXRpb24ub2tpby5Hb2xkZW5WYWx1ZSRQb2ludIRwF2M5CCK2AgACRAABeEQAAXl4cEAgAAAAAAAAQC4AAAAAAAA=

Okio将这个字符串称之为 Golden Value

接下来,我们尝试将这个字符串(Golden Value)反序列化为一个Point对象

首先转回ByteString对象:

ByteString goldenBytes = ByteString.decodeBase64(“rO0ABXNyADVjb20uZXhhbXBsZS5hc3VzLm1”

+“5YXBwbGljYXRpb24ub2tpby5Hb2xkZW5WYWx1ZSRQb2ludIRwF2M5CCK2AgACRAABeEQAAXl4cEAgAAAAAAAAQC4AAAAAAAA=”);

然后将ByteString对象反序列化:

private Object deserialize(ByteString byteString) throws IOException, ClassNotFoundException {

Buffer buffer = new Buffer();

buffer.write(byteString);

try (ObjectInputStream objectIn = new ObjectInputStream(buffer.inputStream())) {

Object result = objectIn.readObject();

if (objectIn.read() != -1) throw new IOException(“Unconsumed bytes in stream”);

return result;

}

}

public void run() throws Exception {

Point point = new Point(8.0, 15.0);

ByteString pointBytes = serialize(point);

System.out.println(pointBytes.base64());

ByteString goldenBytes = ByteString.decodeBase64(“rO0ABXNyADVjb20uZXhhbXBsZS5hc3VzLm15YXBwbGljYXRpb24ub2tpby5Hb2xkZW5WYWx1ZSRQb2ludIRwF2M5CCK2AgACRAABeEQAAXl4cEAgAAAAAAAAQC4AAAAAAAA=”);

Point decoded = (Point) deserialize(goldenBytes);

if (point.x == decoded.x || point.y == decoded.y) {

System.out.println(“Equals”);

}

}

输出:

这样我们可以在不破坏兼容性的情况下更改对象的序列化方式。

这个序列化与Java原生的序列化有一个明显的区别就是GodenValue可以在不同客户端之间兼容(只要序列化和反序列化的Class是相同的)。什么意思呢,比如我在PC端使用Okio序列化一个User对象生成的GodenValue字符串,这个字符串你拿到手机端照样可以反序列化出来User对象。

写二进制文件

编码二进制文件与编码文本文件没有什么不同,Okio使用相同的BufferedSinkBufferedSource字节。这对于同时包含字节和字符数据的二进制格式很方便。写二进制数据比写文本更容易出错,需要注意以下几点:

  • 字段的宽度 ,即字节的数量。Okio没有释放部分字节的机制。如果你需要的话,需要自己在写操作之前对字节进行shift和mask运算。

  • 字段的字节顺序 , 所有多字节的字段都具有结束符:字节的顺序是从最高位到最低位(大字节 big endian),还是从最低位到最高位(小字节 little endian)。Okio中针对小字节排序的方法都带有Le的后缀;而没有后缀的方法默认是大字节排序的。

  • 有符号和无符号,Java没有无符号的基础类型(除了char!)因此,在应用程序层经常会遇到这种情况。为方便使用,Okio的writeByte()writeShort()方法可以接受int类型。你可以直接传递一个“无符号”字节像255,Okio会做正确的处理。

| 方法 | 宽度 | 字节排序 | 值 | 编码后的值 |

| :-- | :-: | :-: | :-: | :-- |

| writeByte | 1 | | 3 | 03 |

| writeShort | 2 | big | 3 | 00 03 |

| writeInt | 4 | big | 3 | 00 00 00 03 |

| writeLong | 8 | big | 3 | 00 00 00 00 00 00 00 03 |

| writeShortLe | 2 | little | 3 | 03 00 |

| writeIntLe | 4 | little | 3 | 03 00 00 00 |

| writeLongLe | 8 | little | 3 | 03 00 00 00 00![](https://www.hualigs.cn/image/61dba891ed8ee.jpg) 00 00 00 |

| writeByte | 1 | | Byte.MAX_VALUE | 7f |

| writeShort | 2 | big | Short.MAX_VALUE | 7f ff |

| writeInt | 4 | big | Int.MAX_VALUE | 7f ff ff ff |

| writeLong | 8 | big | Long.MAX_VALUE | 7f ff ff ff ff ff ff ff |

| writeShortLe | 2 | little | Short.MAX_VALUE | ff 7f |

| writeIntLe | 4 | little | Int.MAX_VALUE | ff ff ff 7f |

| writeLongLe | 8 | little | Long.MAX_VALUE | ff ff ff ff ff ff ff 7f |

下面的示例代码是按照 BMP文件格式 对文件进行编码:

public final class BitmapEncoder {

static final class Bitmap {

private final int[][] pixels;

Bitmap(int[][] pixels) {

this.pixels = pixels;

}

int width() {

return pixels[0].length;

}

int height() {

return pixels.length;

}

int red(int x, int y) {

return (pixels[y][x] & 0xff0000) >> 16;

}

int green(int x, int y) {

return (pixels[y][x] & 0xff00) >> 8;

}

int blue(int x, int y) {

return (pixels[y][x] & 0xff);

}

}

/**

  • Returns a bitmap that lights up red subpixels at the bottom, green subpixels on the right, and

  • blue subpixels in bottom-right.

*/

Bitmap generateGradient() {

int[][] pixels = new int[1080][1920];

for (int y = 0; y < 1080; y++) {

for (int x = 0; x < 1920; x++) {

int r = (int) (y / 1080f * 255);

int g = (int) (x / 1920f * 255);

int b = (int) ((Math.hypot(x, y) / Math.hypot(1080, 1920)) * 255);

pixels[y][x] = r << 16 | g << 8 | b;

}

}

return new Bitmap(pixels);

}

void encode(Bitmap bitmap, File file) throws IOException {

try (BufferedSink sink = Okio.buffer(Okio.sink(file))) {

encode(bitmap, sink);

}

}

/**

  • https://en.wikipedia.org/wiki/BMP_file_format

*/

void encode(Bitmap bitmap, BufferedSink sink) throws IOException {

int height = bitmap.height();

int width = bitmap.width();

int bytesPerPixel = 3;

int rowByteCountWithoutPadding = (bytesPerPixel * width);

int rowByteCount = ((rowByteCountWithoutPadding + 3) / 4) * 4;

int pixelDataSize = rowByteCount * height;

int bmpHeaderSize = 14;

int dibHeaderSize = 40;

// BMP Header

sink.writeUtf8(“BM”); // ID.

sink.writeIntLe(bmpHeaderSize + dibHeaderSize + pixelDataSize); // File size.

sink.writeShortLe(0); // Unused.

sink.writeShortLe(0); // Unused.

sink.writeIntLe(bmpHeaderSize + dibHeaderSize); // Offset of pixel data.

// DIB Header

sink.writeIntLe(dibHeaderSize);

sink.writeIntLe(width);

sink.writeIntLe(height);

sink.writeShortLe(1); // Color plane count.

sink.writeShortLe(bytesPerPixel * Byte.SIZE);

sink.writeIntLe(0); // No compression.

sink.writeIntLe(16); // Size of bitmap data including padding.

sink.writeIntLe(2835); // Horizontal print resolution in pixels/meter. (72 dpi).

sink.writeIntLe(2835); // Vertical print resolution in pixels/meter. (72 dpi).

sink.writeIntLe(0); // Palette color count.

sink.writeIntLe(0); // 0 important colors.

// Pixel data.

for (int y = height - 1; y >= 0; y–) {

for (int x = 0; x < width; x++) {

Okio—— 更加高效易用的IO库,android开发网络编程相关推荐

  1. Okio—— 更加高效易用的IO库

    在OkHttp的源码中经常能看到Okio的身影,所以单独拿出来学习一下,作为OkHttp的低层IO库,Okio确实比传统的java输入输出流读写更加方便高效.Okio补充了java.io和java.n ...

  2. Okio—— 更加高效易用的IO库,太完整了

    System.out.println(line); } } } 写文本文件 public void writeEnv(File file) throws IOException { Sink file ...

  3. Java Okio-更加高效易用的IO库

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/119997762 本文出自[赵彦军的博客] Java IO流学习总结一:输入输出流 ...

  4. 易语言 标准c 动态库,易语言Dll动态库的开发

    一.关于易语言DLL 易语言Dll动态库的开发是编程当中和其他语言开发进行交互必不可少的方法之一,从易语言3.6版开始,已经能够支持对DLL动态链接库的开发, 编译出的DLL是标准的DLL,和其他编程 ...

  5. 标准IO库--unix环境高级编程读书笔记

    标准IO库是C语言提供的一个库,不光存在于linux中,在windows中也是有的.标准IO库和文件IO的不同是,标准IO库是对文件IO(即系统调用)的封装,并且在用户层添加了一些缓冲区. 文件IO的 ...

  6. 基础服务器 IO 模型 Proactor 模型 Reactor 模型 IO 多路复用 异步 IO 模型 Linux 服务器开发 网络编程服务器模型

    本文主要记录服务器的 IO 模型的类型(从多路复用,异步 IO 讲到 Proactor Reactor 模型),包括 Real World nginx 和 apache ,kafka 等分析,配备自洽 ...

  7. 11 | Android 高级进阶(源码剖析篇) Square 高效易用的 IO 框架 okio(四)

    作者简介:ASCE1885, <Android 高级进阶>作者. 本文由于潜在的商业目的,未经授权不开放全文转载许可,谢谢! 本文分析的源码版本已经 fork 到我的 Github. 超时 ...

  8. Linux非阻塞IO(二)网络编程中非阻塞IO与IO复用模型结合

    上文描述了最简易的非阻塞IO,采用的是轮询的方式,这节我们使用IO复用模型. 阻塞IO 过去我们使用IO复用与阻塞IO结合的时候,IO复用模型起到的作用是并发监听多个fd. 以简单的回射服务器为例,我 ...

  9. linux ndk编译静态库,Android开发教程:NDK编译静态库失败

    Android---NDK编译静态库失败 Android.mk 相关编译文件: include $(CLEAR_VARS) LOCAL_MODULE    := libdvnca LOCAL_SRC_ ...

最新文章

  1. 2021年腾讯云安装Docker最简洁方法
  2. 一文详解NDT算法实现
  3. Winform无法加载基类的错误解决
  4. python平均分由高到低顺序排列输出选手编号和最后得分_python 字典的使用案例二:求平均分,并按平均分由高到低输出选手编号和最后得分...
  5. linux命令找目录,linux中何种指令可以查看当前所处的目录位置?
  6. Iplat 4J 6.2 常见问题
  7. java图片上传及图片回显1
  8. winform mysql 客户端_Winform+Mysql登录
  9. 【解决办法】hosts文件被劫持或者看不到办法
  10. 戴尔DCS两周年庆 推新型数据中心设备
  11. matlab设计凸轮轮廓代码_数控铣床搞定曲轴、凸轮轴粗加工的奥秘、高招与绝对细节经验...
  12. 如何使用 FFMpeg 在 Node.js 中将音频从 Wav 转换为 MP3
  13. 优秀的java程序员怎么写注释的
  14. 相机对焦、标定等近期学习
  15. m3u8转mp4,不用格式软件
  16. 爱笑程序员-笑话10则
  17. 那些程序员才懂的笑话
  18. 重启随机游走(RWR)算法
  19. python+requests接口自动化测试框架实例详解教程(米兔888)【适合半年或一年以上天天做自动化经验的大神下载使用】
  20. PHP内核源码阅读过程(四)

热门文章

  1. idea方法注解模板
  2. 免费OA系统给企业带来的价值体现
  3. SQL刷题宝典-MySQL速通力扣困难题
  4. matlab logistic函数表达式,利用MATLAB进行logistic曲线拟合
  5. MySql的复合主键和联合主键的介绍
  6. cad怎么绘制大样图_CAD如何绘制完整的建筑图
  7. STM32 软件模拟IIC
  8. Yearning sql审核平台docker化布署
  9. 用Zabbix和Docker搭建监控平台
  10. yale patt 的计算机系统导论,[转载]Yale N. Patt教授的《计算机系统概论》