Android中使用Nio通道与内存映射

NIO 是non-blocking的简称,在jdk1.4 里提供的新api 。Sun 官方标榜的特性如下: 为所有的原始类型提供(Buffer)缓存支持。字符集编码解码解决方案。 Channel :一个新的原始I/O 抽象。 支持锁和内存映射文件的文件访问接口。 提供多路(non-bloking) 非阻塞式的高伸缩性网络I/O 。

在分析公司前辈写的项目,无意中发现了其中使用到了Java nio相关的东西。于是这几天就在学习Java nio,也看了Ron Hitchens著的《Java NIO》这本书。

复制文件

先来看一个例子:以复制一个大小为2.5M的apk文件为例。

  • 传统IO的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
long startTime = System.currentTimeMillis();
try {
FileInputStream input = new FileInputStream(sourceFile);
BufferedInputStream inbuff = new BufferedInputStream(input);
FileOutputStream out = new FileOutputStream(targetFile);
BufferedOutputStream outbuff = new BufferedOutputStream(out);
byte[] b = new byte[1024];
int len = 0;
while ((len = inbuff.read(b)) != -1) {
outbuff.write(b, 0, len);
}
outbuff.flush();
outbuff.close();
out.close();
inbuff.close();
input.close();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
long endTime = System.currentTimeMillis() - startTime;
Log.d(TAG, "传统IO方式:耗时" + endTime + "ms");
}

调用,输出:

1
10-08 21:09:23.970 2575-2575/com.shoewann.demo D/MainActivity: 传统IO方式:耗时94ms

传统IO的方式:耗时94ms

  • NIO通道的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
long startTime = System.currentTimeMillis();
try {
//文件(输入、输出) Stream流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(targetFile);
//文件(输入、输出) Channel通道
FileChannel fcIn = fis.getChannel();
FileChannel fcOut = fos.getChannel();
//从文件输入通道传输byte数据到文件输出通道
fcIn.transferTo(0, fcIn.size(), fcOut);
//关闭文件(输入、输出) Channel通道
fcOut.close();
fcIn.close();
//关闭文件(输入、输出) Stream流
fos.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
long endTime = System.currentTimeMillis() - startTime;
Log.d(TAG, "nio通道方式:耗时" + endTime + "ms");
}

调用,输出:

1
10-08 21:09:26.103 2575-2575/com.shoewann.demo D/MainActivity: nio通道方式:耗时79ms

NIO通道的方式:耗时79ms
速度比传统IO的方式快,当然比起NIO通道复制,下面的这种内存映射的方式还更快

  • 内存映射的方式
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
long startTime = System.currentTimeMillis();
try {
//创建源文件和目标文件的随机访问文件对象
RandomAccessFile raf = new RandomAccessFile(srcFile, "r");
RandomAccessFile wraf = new RandomAccessFile(targetFile, "rw");
//创建文件Channel通道
FileChannel in = raf.getChannel();
FileChannel out = wraf.getChannel();
//源文件的通道大小
long size = in.size();
//内存映射通道
MappedByteBuffer inBf = in.map(FileChannel.MapMode.READ_ONLY, 0, size);
//写入到输出通道
out.write(inBf);
//关闭输入输出通道
in.close();
out.close();
//关闭随机访问文件对象
raf.close();
wraf.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
long endTime = System.currentTimeMillis() - startTime;
Log.d(TAG, "内存映射文件方式:耗时" + endTime + "ms");
}

调用,输出:

1
10-08 21:09:28.168 2575-2575/com.shoewann.demo D/MainActivity: 内存映射文件方式:耗时35ms

内存映射的方式:耗时35ms

通过以上的示例,可以看出对文件进行复制操作,内存映射文件的方式是最快的,nio通道方式仅次其后,传统IO方式相对前面两种较慢。但是,内存映射文件和nio通道的方式都是基于传统IO的方式进行衍生而来。

读文件

1
2
3
4
5
6
7
8
9
10
11
12
RandomAccessFile rRaf = new RandomAccessFile(file, "r");
FileChannel fcIn = rRaf.getChannel();
long size = fcIn.size();
int len = (int) file.length();
byte[] bytes = new byte[len];
MappedByteBuffer inBf = fcIn.map(FileChannel.MapMode.READ_ONLY, 0, size);
for (int i = 0; i < len; i++) {
bytes[i] = inBf.get(i);//取出每个元素
}
String str = new String(bytes, "utf-8");
fcIn.close();
rRaf.close();

写文件

1
2
3
4
5
6
RandomAccessFile wRaf = new RandomAccessFile(file, "rw");
FileChannel fcOut = wRaf.getChannel();
//fcOut.position(fc.size()); // 移动到文件末尾,进行追加
fcOut.write(ByteBuffer.wrap(encrypt.getBytes()));
fcOut.close();
wRaf.close();

以上就是Android中使用Nio通道与内存映射的示例,如有不足,欢迎指正,谢谢。

if (本文对您有用) { Pay (请随意¥打赏) } else { Commit feedback (底部评论区提交建议、反馈) } 感谢支持!