上代码:
private void read() {
Source source = null;
BufferedSource buffer = null;
try {
testFile = new File(Environment.getExternalStorageDirectory(), "test.txt");
source = Okio.source(testFile);
buffer = Okio.buffer(source);
/** Removes all bytes from this, decodes them as {@code charset}, and returns the string. */
/** readByteString()系列会返回指定编码的String,同时也会从buffer中移除相应的byteString(不传参数表示全部取出然后移除)。
* 下面的方法也是一样,只不过移除的字节数量是调用者控制的
* 注:若test.txt的bytes字节数远远低于2000,故而只单独调用最后一句也会报java.io.EOFException*/
//System.out.println("String = " + buffer.readString(Charset.forName("UTF-8")));
//System.out.println("readByteString = " + buffer.readByteString());
//System.out.println("readByteString20 = " + buffer.readByteString(20));
System.out.println("readByteString2000 = " + buffer.readByteString(2000));
} catch (Exception e) {
System.out.println("exception = " + e.getMessage());
e.printStackTrace();
} finally {
try {
/**
* close()的注释:
* Closes this source and releases the resources held by this source. It is an
* error to read a closed source. It is safe to close a source more than once.
*/
if (buffer != null ) {
buffer.close();
}
if (source != null) {
source.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
上面的代码回抛出java.io.EOFException异常。进行分析:
- buffer = Okio.buffer(source)的具体实现:
/**
* Returns a new source that buffers reads from {@code source}. The returned
* source will perform bulk reads into its in-memory buffer. Use this wherever
* you read a source to get an ergonomic and efficient access to data.
* 大概就是说从根据source得到一个新的source,新的source可以执行批量读进自己的缓冲区。
* 这是很高效的
*/
public static BufferedSource buffer(Source source) {
return new RealBufferedSource(source);
}
@Override
public ByteString readByteString(long byteCount) throws IOException {
require(byteCount);
return buffer.readByteString(byteCount);
}
@Override
public void require(long byteCount) throws IOException {
if (!request(byteCount)) throw new EOFException();
}
@Override
public boolean request(long byteCount) throws IOException {
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
if (closed) throw new IllegalStateException("closed");
while (buffer.size < byteCount) {
if (source.read(buffer, Segment.SIZE) == -1) return false;
}
return true;
}
上面的例子中,第一次进来的时候,buffer.size() = 0(注:buffer是new 出来的,即 public final Buffer buffer = new Buffer();),此时While循环成立。source是通过source = Okio.source(testFile);这句代码返回的,内部实现是:
return new Source() {
@Override public long read(Buffer sink, long byteCount) throws IOException {
...
try {
Segment tail = sink.writableSegment(1);
int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit);
int bytesRead = in.read(tail.data, tail.limit, maxToCopy);
if (bytesRead == -1) return -1;
tail.limit += bytesRead;
sink.size += bytesRead;
return bytesRead;
} catch (AssertionError e) {
if (isAndroidGetsocknameError(e)) throw new IOException(e);
throw e;
}
}
...
};
由于我的test.txt中的字节数量为46,首次循环会将source中的46个字节全部存进buffer中返回,再次执行while循环(因为46<2000),执行到 int bytesRead = in.read(tail.data, tail.limit, maxToCopy);时,注意此处的tail,是一个双向环形链表。tail.limit的值为46,而tail.data的偏移量为46之后再无数据,故而返回-1,即request(long)返回false,require(long)抛出EOFException异常。
网友评论