系列文章:
Android NIO 系列教程(一) NIO概述
Android NIO 系列教程(二) – Channel
Android NIO 系列教程(三) – Buffer
Android NIO 系列教程(四) – Selector
Android NIO 系列教程(五) – FileChannel
Android NIO 系列教程(六) – SocketChannel
Android NIO 系列教程(七) – ServerSocketChannel
Android NIO 系列教程(八) --NIO简易聊天室
Java NIO中的Buffer用于和NIO通道进行交互。如你所知,数据是从通道读入缓冲区,从缓冲区写入到通道中的。
缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。
Buffer 基础使用
使用 buffer 进行读写,一般需要以下4个步骤:
- 写数据到 buffer
- 调用 buffer.flip() 切换模式方法
- 从 buffer 读取数据
- 调用 buffer.clear() 或者 buffer.compat()方法
当你写数据到 buffer,buffer 将会记录你写入了多少数据,当你需要读取数据,你需要调用 flip() 方法,从写模式切换到 读模式,在读模式下,你可以读取buffer 所有的数据。
当你读完所有数据后,你需要调用清除buffer 的数据,这样才能继续写入。清除有两种方法,调用clear()或者compat() 方法;
- clear() : clear 方法会清除 buffer 中所有的数据
- compat() : compat 则是清除你所读过的数据,那些未读取的数据,将会被移动初始位置,当有数据写入时,则在它之后的位置被写入,例子可以参考上面的 fileChannel 。
Buffer 的 Capacity, Position 和 Limit
Buffer 本质上一块你可以写入和读取数据的内存块,这个内存块被包裹成 NIO Buffer 对象,并提供一系列方法,以便我们访问这块内存。
为了数据 Buffer 的原理,我们需要认识它的三个组成部分:
- capacity
- position
- limit
capacity 表示 buffer 的容量,当数据到达buffer的容量大小时,你需要使用 clear 或者 compat 才能继续写入数据;
而 position 和 limit 则取决于 buffer 的模式:
position:
- 写模式: position 表示当前位置,起始位置为 0,当写入一个 byte,long 数据后,position 会移动对应的字节数,即position会向前移动到下一个可插入数据的Buffer单元,position 的最大值 为 capacity - 1
- 读模式:postion 表示某个特定位置,当将Buffer从写模式切换到读模式,position会被重置为0. 当从Buffer的position处读取数据时,position向前移动到下一个可读的位置
limit:
- 写模式:表示可以向 buffer 写入的最大数据,即 limit == capacity
- 读模式:表示你能读到多少数据;当切换到读模式时,limit 相当于 写模式下的 position;换句话说,写入多少数据 position,你就能读到多少数据 limit
如下图:
Buffer 的基础数据类型
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
这些Buffer类型代表了不同的数据类型。换句话说,就是可以通过char,short,int,long,float 或 double类型来操作缓冲区中的字节。
Buffer 创建
Buffer 的创建有两种方式,,如下:
// 使用 allocate 创建个 48 个字节的 buffer
ByteBuffer buf = ByteBuffer.allocate(48);
// 使用 wrap 创建个 48 个字节的 buffer
byte[] bytes = new byte[48];
ByteBuffer buf2 = ByteBuffer.wrap(bytes, 0, bytes.length);
通过 wrap ,我们可以很方便的把 byte 转换成 buffer 去使用,从而拿到数据。
向 Buffer 写数据
向 buffer 写数据有两种方式:
- 通过 channel 写数据
- 调用 bufffer 的 put 方法
如下:
//通过 channel
int bytesRead = inChannel.read(buf); //read into buffer.
//通过 put
buf.put(127);
put 有很多版本,具体可以参考 javadoc。
flip()
flip 方法,会让 buffer 从写模式切换 读模式,调用 flip ,会把 position 重置为 0,limit 则表示写入多少个字节。
向 Buffer 读数据
向 buffer 读数据也有两种方式:
- 通过channel 读数据
- 通过 buffer 的 get() 方法
//read from buffer into channel.
int bytesWritten = inChannel.write(buf);
//通过 get
byte aByte = buf.get();
get 也有很多中方法,比如 getInt 等,具体参考 javadoc。
rewind()
该方法会把 position 重置为0,所以你可以重读所有数据,limit 则不受影响。
clear() and compact()
2.1 已解释,这里就不过多累赘
mark() and reset()
通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position;之后可以通过调用Buffer.reset()方法恢复到这个position。例如:
//表示 position,假如 position为2
buffer.mark();
// 调用 reset 之后,position 不是从0开始,而是从 2
buffer.reset();
equals() and compareTo()
可以使用equals()和compareTo()方法两个Buffer。
equals()
当满足下列条件时,表示两个Buffer相等:
- 有相同的类型(byte、char、int等)。
- Buffer中剩余的byte、char等的个数相等。
- Buffer中所有剩余的byte、char等都相同。
equals只是比较Buffer的一部分,不是每一个在它里面的元素都比较。实际上,它只比较Buffer中的剩余元素。
compareTo()
- compareTo()方法比较两个Buffer的剩余元素(byte、char等), 如果满足下列条件,则认为一个
-
- Buffer“小于”另一个Buffer:
第一个不相等的元素小于另一个Buffer中对应的元素 。
所有元素都相等,但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)。
下一章,我们将学习 selector