网络操作很大一部分功能就是输入和输出数据。
简单归纳就是上传和下载文件。文件也是数据的一种载体。
java对数据的操作归并为流。
所以对于数据流的操作定义2个基本类。
java.io.OutputStream
java.io.InputStream
一:OutputStream & InputStream
输出流。也就是写入数据,在这里有个易被忽视的方法:flush。
public void flush() throws IOException { }
这个方法的作用,我们每次写一个字符到文件或者其他地方是十分冗余的,如果可以,那就有一个缓存,当我数据达到一定量的时候,
才开始写入的操作。
所以调用:
public void write(byte b[], int off, int len) throws IOException
不一定会立刻把数据写入到文件中,这个时候如果close,这部分数据就会丢失掉。
而flush的作用就是,在缓存区,不管数据是否达到一个临界值,强制把数据写入到文件中!
输入流,与输出流相对的。
也有很多方法。最重要的当然是read。
读取单个字符的方式,显然不是很好的一个方式。
@Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { return IoBridge.read(fd, buffer, byteOffset, byteCount); }
以上就是android里面java/io/FileInputStream.java 读取多个字符的方法。
由于网络会比较忙,所以通常传输1024字节的内容,可能只收到512个,剩余部分还在传输过程中。
二:缓冲流
BufferedInputStream和BufferedOutputStream
他们会对inputStream & OutputStream做缓存,然后提高传输的性能。
对于网络传输来说,一次传递1个字节,但是它要包40个字节的头,所以1K的数据会变成41K,
而如果打包在一次,则只要1K多点的数据就可以,性能显然会差很多。
@Override public synchronized int read() throws IOException { // Use local refs since buf and in may be invalidated by an // unsynchronized close() byte[] localBuf = buf; InputStream localIn = in; if (localBuf == null || localIn == null) { throw streamClosed(); } /* Are there buffered bytes available? */ if (pos >= count && fillbuf(localIn, localBuf) == -1) { return -1; /* no, fill buffer */ } // localBuf may have been invalidated by fillbuf if (localBuf != buf) { localBuf = buf; if (localBuf == null) { throw streamClosed(); } } /* Did filling the buffer fail with -1 (EOF)? */ if (count - pos > 0) { return localBuf[pos++] & 0xFF; } return -1; }
关键就是fillbuf这个操作,所以read方法每次读取的都是一段字节,而不是依次读取的。
private int fillbuf(InputStream localIn, byte[] localBuf) throws IOException { if (markpos == -1 || (pos - markpos >= marklimit)) { /* Mark position not set or exceeded readlimit */ int result = localIn.read(localBuf); if (result > 0) { markpos = -1; pos = 0; count = result == -1 ? 0 : result; } return result; } if (markpos == 0 && marklimit > localBuf.length) { /* Increase buffer size to accommodate the readlimit */ int newLength = localBuf.length * 2; if (newLength > marklimit) { newLength = marklimit; } byte[] newbuf = new byte[newLength]; System.arraycopy(localBuf, 0, newbuf, 0, localBuf.length); // Reassign buf, which will invalidate any local references // FIXME: what if buf was null? localBuf = buf = newbuf; } else if (markpos > 0) { System.arraycopy(localBuf, markpos, localBuf, 0, localBuf.length - markpos); } /* Set the new position and mark position */ pos -= markpos; count = markpos = 0; int bytesread = localIn.read(localBuf, pos, localBuf.length - pos); count = bytesread <= 0 ? pos : pos + bytesread; return bytesread; }
可以看到fillbuf里面会调用
int bytesread = localIn.read(localBuf, pos, localBuf.length - pos);
所以buffer 会把stream放入到一个byte[]的数组里面。
三:压缩流:
DeflaterOutputStream InflaterInputStream GZIPOutputStream GZIPInputStream ZipOutputStream ZipInputStream
这六个就是java提供的压缩流,他们的读写就是使用read & write方法。
输出流压缩数据,输入流解压数据。
四:摘要流
DigestInputStream,DigestOutputStream
摘要流的目的,主要是可以验证数据的完整性。
虽然程序员可以更高效的写出独立的验证算法,但是统一使用以后,可以简化这部分的操作。
五:阅读器和书写器
Reader -> BufferedReader -> LineNumberReader -> InputStreamReader -> FileReader Writer -> BufferedWriter -> OutputStreamWriter -> FileWriter
网络传输中有各种不同的字符编码,所以就会需要有一个读写和处理的工具来操作流。
FileReader & FileWrite是处理文件的,具体的细节就不写了!