Android开发进阶之NIO非阻塞包(二)
有关Android NIO我们主要分为三大类,ByteBuffer、FileChannel和SocketChannel。由于篇幅原因今天Android123只对前两个做说明。NIO和传统的I/O比较大的区别在于传输方式非阻塞,一种基于事件驱动的模式,将会使方法执行完后立即返回,传统I/O主要使用了流Stream的方式,而在New I/O中,使用了字节缓存ByteBuffer来承载数据。
ByteBuffer位于java.nio包中,目前提供了Java基本类型中除Boolean外其他类型的缓冲类型,比如ByteBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer和ShortBuffer 。同时还提供了一种更特殊的映射字节缓冲类型MappedByteBuffer。在传统IO的输入输出流中,InputStream中只提供了字节型或字节数组的访问对应NIO就是ByteBuffer,但是处理传统的DataInputStream的int等类型,就是IntBuffer,但是缓冲类型并没有提供UTF这样的类型处理,所以我们仍然需要使用ByteBuffer处理字符串,但是NIO提供了一个封装的类在java.nio.charset包中,通过字符的编码CharsetEncoder和解码CharsetDecoder类来处理字符串,同时这些类可以方便转换编码比如GBK或UTF等等。
一、ByteBuffer类
1) 实例化
直接使用ByteBuffer类的静态方法static ByteBuffer allocate(int capacity) 或 static ByteBuffer allocateDirect(int capacity) 这两个方法来分配内存空间,两种方法的区别主要是后者更适用于繁复分配的字节数组。而 put(ByteBuffer src) 可以从另一个ByteBuffer中构造,也可以通过wrap方法从byte[]中构造,具体参考下面的类型转化内容。
2) 类型转化
ByteBuffer可以很好的和字节数组byte[]转换类型,通过执行ByteBuffer类的final byte[] array() 方法就可以将ByteBuffer转为byte[]。从byte[]来构造ByteBuffer可以使用wrap方法,目前Android或者说Java提供了两种重写方法,比如为static ByteBuffer wrap(byte[] array) 和 static ByteBuffer wrap(byte[] array, int start, int len) ,第二个重载方法中第二个参数为从array这个字节数组的起初位置,第三个参数为array这个字节数组的长度。
3) 往ByteBuffer中添加元素
目前ByteBuffer提供了多种put重写类型来添加,比如put(byte b) 、putChar(char value) 、putFloat(float value) 等等,需要注意的是,按照Java的类型长度,一个byte占1字节,一个char类型是2字节,一个float或int是4字节,一个long则为8字节,和传统的C++有些区别。所以内部的相关位置也会发生变化,同时每种方法还提供了定位的方法比如ByteBuffer put(int index, byte b)
4) 从ByteBuffer中获取元素
同上面的添加想法,各种put被换成了get,比如byte get() 、float getFloat() ,当然了还提供了一种定位的方式,比如double getDouble(int index)
5) ByteBuffer中字节顺序
对于Java来说默认使用了BIG_ENDIAN方式存储,和C正好相反的,通过
final ByteOrder order() 返回当前的字节顺序。
final ByteBuffer order(ByteOrder byteOrder) 设置字节顺序,ByteOrder类的值有两个定义,比如LITTLE_ENDIAN、BIG_ENDIAN,如果使用当前平台则为ByteOrder.nativeOrder()在Android中则为 BIG_ENDIAN,当然如果设置为order(null) 则使用LITTLE_ENDIAN。
二、FileChannel类
在NIO中除了Socket外,还提供了File设备的通道类,FileChannel位于java.nio.channels.FileChannel包中,在Android SDK文档中我们可以方便的找到,对于文件复制我们可以使用ByteBuffer方式作为缓冲,比如
String infile = "/sdcard/cwj.dat";
String outfile = "/sdcard/android123-test.dat";
FileInputStream fin = new FileInputStream( infile );
FileOutputStream fout = new FileOutputStream( outfile );
FileChannel fcin = fin.getChannel();
FileChannel fcout = fout.getChannel();
ByteBuffer buffer = ByteBuffer.allocate( 1024 ); //分配1KB作为缓冲区
while (true) {
buffer.clear(); //每次使用必须置空缓冲区
int r = fcin.read( buffer );
if (r==-1) {
break;
}
buffer.flip(); //写入前使用flip这个方法
fcout.write( buffer );
}
flip和clear这两个方法是java.nio.Buffer包中,ByteBuffer的父类是从Buffer类继承而来的,这点Android123要提醒大家看Android SDK文档时注意Inherited Methods,而JDK的文档就比较直接了,同时复制文件使用FileChannel的transferTo(long position, long count, WritableByteChannel target) 这个方法可以快速的复制文件,无需自己管理ByteBuffer缓冲区。明天Android开发网介绍NIO主要的Socket相关的内容。