Android内核及各种干货Android技术知识Android知识

Android下的IO库-Okio源码解析(叁)

2017-03-20  本文已影响85人  非墨Zero

上一篇我们着重讲了Okio对输入流的处理,同时也讲了Okio的页式内存管理。本章将着重讲一下Okio的输出操作,由于输出造作和输入操作本身就有很多相似点,因此本章将简单过一下输出流。

Okio的输出流是通过Okio.sink方法构造:

public static Sink sink(OutputStream out) {
    return sink(out, new Timeout());
  }

 private static Sink sink(final OutputStream out, final Timeout timeout) {
    if (out == null) throw new IllegalArgumentException("out == null");
    if (timeout == null) throw new IllegalArgumentException("timeout == null");

    return new Sink() {
      @Override public void write(Buffer source, long byteCount) throws IOException {
        checkOffsetAndCount(source.size, 0, byteCount);//安全性验证
        while (byteCount > 0) {
          timeout.throwIfReached();//超时检测
          Segment head = source.head;
          int toCopy = (int) Math.min(byteCount, head.limit - head.pos);
          out.write(head.data, head.pos, toCopy);

          head.pos += toCopy;
          byteCount -= toCopy;
          source.size -= toCopy;

          if (head.pos == head.limit) {//当head读取完毕,回收掉内存碎片
            source.head = head.pop();
            SegmentPool.recycle(head);
          }
        }
      }

      @Override public void flush() throws IOException {
        out.flush();
      }

      @Override public void close() throws IOException {
        out.close();
      }

      @Override public Timeout timeout() {
        return timeout;
      }
    };
  }

1.sink方法一样是构造了一个Sink接口的匿名类,其中最重要的就是 write(Buffer source, long byteCount) 。
2.按照我们之前对Okio的认识,实际上,Okio只是在java的stream平台上构建的一层装饰库,因此,不论是flush操作还是write操作,最终都是要调用到stream.flush或者stream.write方法。
3.Sink接口的目的,就是将存在于内存Buffer对象中的数据,write到输出流中。

那么复制多少呢?

int toCopy = (int) Math.min(byteCount, head.limit - head.pos);

“toCopy”变量用于记录所需要复制的数据长度,其中byteCount代表调用时候传入的数据长度,head.limit代表head数据片中有的数据,head.pos指向还未读取的数据位置。由于Okio的读取是分块读取的(存储也是分块的),因此,在块中所能读取的数据长度不能超过limit-pos。就这样,sink就完成了从Buffer到Output流的数据传递。

实际上,我们在讲输入和输出的时候,我们一直忽略了一个参数,那就是超时参数:Timeout对象。我们来看下Okio是如何实现的Timeout机制:

// code Sink.write
   while (byteCount > 0) {
          timeout.throwIfReached();//超时检测
          ....
   }

//code Source.read
  while (byteCount > 0) {
         timeout.throwIfReached(); 
         ....
  }

我们看到,对于匿名类的Source和Sink对象,实际上都采用轮询的方式来检测超时,并且调用timeout的throwIfReached方法。

 public void throwIfReached() throws IOException {
    if (Thread.interrupted()) {
      throw new InterruptedIOException("thread interrupted");
    }

    if (hasDeadline /*判断是否有超时限制*/&& deadlineNanoTime - System.nanoTime() <= 0/*时间是否超时*/) {
      throw new InterruptedIOException("deadline reached");
    }
  }

当hasDeadline为true且超过deadlineNanoTime记录的结束时间,sink的write操作或者source的read操作会抛出中断异常。

上一篇下一篇

猜你喜欢

热点阅读