OkHttp源码详解之Okio源码详解

Android之OKHttp源码解析 基本的使用流程如下 二、使用流程源码分析 这里与同步请求的有些不同那异步请求流程中Dispatcher中做了什么?从上面的执行流程源码我们可以了解到在进行同步请求 时只是调用了Dispatcher的方法,将同步请求放置到同步双端队列中,这里并没有体现"分发"的作用,而在进行异步请求时,调用了Dispatcher的方法,这里接着看源码: 首先会将异步任务存储到等待队列中,其后会调用进行任务的分发,根据执行队列的请求数量最大阈值和请求相同host的数量进行一个条件判断,根据结果决定要不要把任务放置执行队 阅读详情

请在电脑上阅读,效果更佳

本文将从两个技术点讲解OkHttp
1. 讲解Okio,因为Okhttp的IO操作都是基于Okio,抛开Okio的OkHttp讲解是不完美的
2. 讲解OkHttp源码

Okio

1. Okio简介

引用官方的一段介绍

Okio是一个补充java.io和java.nio的库,使访问,存储和处理数据变得更加容易。 它最初是作为Android中包含的功能强大的HTTP客户端OkHttp的一个组件。 它运作良好,随时准备解决新问题。

2. 从HelloWorld开始

我们知道,在java.io中InputStream和OutputStream分别代表了输入流,和输出流。相应的在Okio中Source和Sink分别代表了输入流和输出流。接下来我们分别用java.io和Okio实现打印文本内容功能

假设有个文件helloworld.txt文件内容如下

Hello World!
Hello World!
  • java.io实现打印功能
try {
    File file = new File("helloworld.txt");
    FileInputStream fileInputStream = new FileInputStream(file);//1
    byte[] buffer = new byte[(int) file.length()];//2
    fileInputStream.read(buffer);//3
    System.out.write(buffer);//4
} catch (Exception e) {
    e.printStackTrace();
}
  • Okio实现打印功能
try {
    File file = new File("helloworld.txt");
    Source source = Okio.source(file);//a
    Buffer buffer = new Buffer();//b
    source.read(buffer, file.length());//c
    Sink sink = Okio.sink(System.out);
    sink.write(buffer, file.length());//d
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

上面两段代码实现的功能都是一样的,实现思路总结如下
1. 获取文件的输入流 //1和//a处实现
2. 将文件输入流读取到缓冲区 //3和//c处实现
3. 将缓冲区的数据写入到Sytem.out流中 //4和//d处

image

3.Source Sink源码讲解

1. Okio Source Sink

从上面的例子我们可以把Source想象成InputStream,把Sink想象成OutputStream。通过下面的图片,我们来看下Source和Sink的定义
image

  • Source通过read(Buffer sink,long byteCount)方法,把磁盘,网络,或者内存中的输入流的数据读取到内存的Buffer中。
  • Sink刚好相反,它通过write(Buffer source,long byteCount)方法把内存Buffer中的数据写入到输出流中。
  • Okio中定义了生成Source的静态方法,source(File file)、source(InputStream in)、source(InputStream in,Timeout timeout)、source(Socket socket)。其中source(Socket socket)在OkHttp中被用来操作网络请求的Response。这很重要是OkHttp IO操作的核心。这四个重载方法真正的实现是在source(InputStream in,Timeout timeout)中
  • Okio中定义了生成Sink的静态方法,sink(File file)、sink(OutputStream out)、source(OutputStream out,Timeout timeout)、source(Socket socket)。其中source(Socket socket)在OkHttp中被用来操作网络请求的Request。同样这也是OkHttp IO操作的核心。这四个重载方法真正的实现是在sink(OutputStream out,Timeout timeout)中
2. Source Sink的创建
2.1 Okio source(InputStream in,Timeout timeout)
private static Source source(final InputStream in, final Timeout timeout) {
    if (in == null) throw new IllegalArgumentException("in == null");
    if (timeout == null) throw new IllegalArgumentException("timeout == null");

    return new Source() {
      @Override public long read(Buffer sink, long byteCount) throws IOException {
        if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
        if (byteCount == 0) return 0;
        try {
          timeout.throwIfReached();
          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;
        }
      }

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

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

      @Override public String toString() {
        return "source(" + in + ")";
      }
    };
  }

仔细看一眼代码,除了int bytesRead = in.read(tail.data, tail.limit, maxToCopy);看着眼熟,其它的代码如Segment tail = sink.writableSegment(1);初学者表示很懵逼呀。实话告诉各位,整个Okio的精髓就在这两行代码里。这才叫四两拨千斤。好吧,让我们来重温一下InputStream的read(byte[] b, int off, int len)方法

read
public int read(byte[] b,
       int off,
       int len)
         throws IOException
Reads up to len bytes of data from the input stream into an array of bytes. An attempt is made to read as many as len bytes, but a smaller number may be read. The number of bytes actually read is returned as an integer.
This method blocks until input data is available, end of file is detected, or an exception is thrown.

翻译如下:从输入流中读取len个字节到字节数组b中。这个方法可能会被阻塞,直到输入流数据可用。

如此说来tail.data应该是个byte[],tail.limit是读取流的起始位置,maxToCopy是要读取的字节的长度了。没错是这样的。

Segment tail = sink.writableSegment(1);这句代码目前我还是看不懂啊,Segment是什么呀?它和Buffer之间的关系是什么呀?上图!一图抵千言
image
image

虽然说一图抵千言,还是做个简单的讲解吧。

  • Segment说白了就是byte[],每个绿色的或者白色的小方格代表一个byte。绿色表示已经有数据了,白色表示没有数据。pos指向第一个绿色的格子,表示读取数据的位置,limit指向第一个白色的格子,表示写入数据的位置。
  • Buffer是一个由Segment组成的双链表。每一个Segment最多可以容下8192个字节。在向Buffer的Segment写入数据时,如果超过了8192个字节,那么会从SegmentPool(一个对象池,最多可以容下8个Segment)拿一个Segment或者新建一个Segment(因为SegmentPool中的对象都被用光了)加入到双链表的尾端

接下来我们来分析下Segment源码,毕竟Talk Is Cheap,Show Me The Code。由于Segment代码还是比较简单的。所以我就在源码中加入注释来讲解

final class Segment {
  /** 每个Segment最大容量8KB */
  static final int SIZE = 8192;

  /** Segment分两种,只读和可写。当Segment需要被拆分成两个小的Segment的时候,如果被拆分
   * 出去的Segment的大小超过1024,那么那个Segment会被定义成只读的。(暂时不理解没关系)
  */
  static final int SHARE_MINIMUM = 1024;

  /**真正存储数据的byte数组**/
  final byte[] data;

  /** 读取数据的地方 参考前面的图片解释 */
  int pos;

  /** 写数据的地方 参考前面的图片解释 */
  int limit;

  /** 只读模式 */
  boolean shared;

  /** 可写模式 */
  boolean owner;

  /** 双链表的next指针 */
  Segment next;

  /** 双链表的prev指针 */
  Segment prev;

  Segment() {
    this.data = new byte[SIZE];
    this.owner = true;//默认是可写的
    this.shared = false;
  }

  /**当前Segment从双链表中出队**/
  public Segment pop() {
    Segment result = next != this ? next : null;
    prev.next = next;
    next.prev = prev;
    next = null;
    prev = null;
    return result;
  }

  /**插入一个新的Segment到当前的Segment的后面**/
   public Segment push(Segment segment) {
    segment.prev = this;
    segment.next = next;
    next.prev = segment;
    next = segment;
    return segment;
  }

    /**
    *把当前的Segment分成两个Segment。
    *使用场景 把当前Segment A写入到Segment B中。将设A中数据的大小是2KB(记得容量是8KB)
    *B中的数据是7KB(剩余空间1KB),这样A往B中写数据,肯定是写不完的,需要把A分成
    *A1(新建的Segment)和A(原来的A,需要更新pos)
    **/
   public Segment split(int byteCount) {
    if (byteCount <= 0 || byteCount > limit - pos) throw new IllegalArgumentException();
    Segment prefix;

    if (byteCount >= SHARE_MINIMUM) {
    //如果写入的数据超过1kb 新建一个只读的Segment,避免arrayCopy
      prefix = new Segment(this);
    } else {
    //从SegmentPool中拿一个Segment
      prefix = SegmentPool.take();
      System.arraycopy(data, pos, prefix.data, 0, byteCount);
    }

    prefix.limit = prefix.pos + byteCount;
    pos += byteCount;
    //插入到当前Segment A的前面 A1->A
    prev.push(prefix);
    return prefix;
  }

  /**对多个Segment的空间做压缩,用来HashSource,HashSink,GzipSource,
  * GzipSink(还怕别人问你Gzip在OkHttp中的实现原理吗)
  **/
  public void compact() {
    //如果只有一个Segment 不需要压缩
    if (prev == this) throw new IllegalStateException();
    // 如果前面的Segment是只读的,没法压缩
    if (!prev.owner) return; 
    int byteCount = limit - pos;
    int availableByteCount = SIZE - prev.limit + (prev.shared ? 0 : prev.pos);
    //两个Segment的总大小不大于8kb 可以合并成一个,否则返回
    if (byteCount > availableByteCount)
    //把当前的Segment的数据写入到前面的Segment中
    writeTo(prev, byteCount);
    //当前的Segment出队列
    pop();
    //回收Segment
    SegmentPool.recycle(this);
  }

  /**写byteCount个数据到sink中**/
  public void writeTo(Segment sink, int byteCount) {
    if (!sink.owner) throw new IllegalArgumentException();
    if (sink.limit + byteCount > SIZE) {
      // We can't fit byteCount bytes at the sink's current position. Shift sink first.
      if (sink.shared) throw new IllegalArgumentException();
      if (sink.limit + byteCount - sink.pos > SIZE) throw new IllegalArgumentException();
      System.arraycopy(sink.data, sink.pos, sink.data, 0, sink.limit - sink.pos);
      sink.limit -= sink.pos;
      sink.pos = 0;
    }

    System.arraycopy(data, pos, sink.data, sink.limit, byteCount);
    sink.limit += byteCount;
    pos += byteCount;
  }
}

总结下Segment的知识

  • Segment其实就是个byte[]
  • Segemnt记录了byte的读写指针pos和limit
  • Segment维护一个双链表

接下来我们来分析 Segment tail = sink.writableSegment(1)

Buffer.java

Segment writableSegment(int minimumCapacity) {
    if (minimumCapacity < 1 || minimumCapacity > Segment.SIZE) throw new IllegalArgumentException();
    //如果buffer 还没有初始化,从对象池拿一个Segment,同时初始化双链表
    if (head == null) {
      head = SegmentPool.take(); // Acquire a first segment.
      return head.next = head.prev = head;
    }
    //拿到双链表的最后一个Segment
    Segment tail = head.prev;
    //判断最后tail的空间够不够,tail是不是只读的Segment
    if (tail.limit + minimumCapacity > Segment.SIZE || !tail.owner) {
    //如果空间不够,或者是只读的 重新拿一个Segment放入到链表尾部
      tail = tail.push(SegmentPool.take()); 
    }
    return tail;
  }

skink.writeableSegment(1)的功能就是,从Buffer 的Segment链表中取到链表最后一个Segment,这个Segment需要满足两个条件1.可写 2.可写空间大于1个字节

到这里咱们基本上把int bytesRead = in.read(tail.data, tail.limit, maxToCopy)和Segment tail = sink.writableSegment(1)讲解清楚了。那么我们再重新看下Okio.source(InputStream in,Timeout timeOut) return new Source()代码块的read方法

@Override public long read(Buffer sink, long byteCount) throws IOException {
        if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
        if (byteCount == 0) return 0;
        try {
        //支持超时读取
          timeout.throwIfReached();
          //拿到buffer中链表的最后一个可写的Segment
          Segment tail = sink.writableSegment(1);
          //获取最大能往tail中写多少个字节
          int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit);
          //计算往Segment写了多少数据(为什么是写,对buffer来说就是写)
          int bytesRead = in.read(tail.data, tail.limit, maxToCopy);
          if (bytesRead == -1) return -1;
          //更新写的位置
          tail.limit += bytesRead;
          //增加buffer的数据总量
          sink.size += bytesRead;
          return bytesRead;
        } catch (AssertionError e) {
          if (isAndroidGetsocknameError(e)) throw new IOException(e);
          throw e;
        }
      }

总结下Okio.source(InputStream in, Timeout timeout)

  • 从Buffer(sink)中找到链表中最后一个可写的并且还有写入空间的Segment记做tail
  • 判断最多能写多少数据到Buffer(sink)中记做maxToCopy
  • 从InputStream(in)中读取maxToCopy个数据到tail中

根据第三条的结论来看,比如你调用了soure.read(buffer,10*1024),那其实返回的肯定是比 10*1024少。举例说明,拿前面的helloworld举例。现在我从网络粘贴了 老罗android开发之旅的一篇文章到helloword.txt里并重复了3遍。文件大小为35243个字节

try {
            File file = new File("helloworld.txt");
            System.out.println("file.length "+file.length());
            Source source = Okio.source(file);
            Sink sink = Okio.sink(System.out);
            Buffer buffer = new Buffer();
            source.read(buffer, file.length());
            System.out.println(buffer.size());
//            sink.write(buffer, file.length());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

执行输出结果如下

file.length 35243
buffer size 8192

如果需要正确的把所有数据都写入到buffer中就需要用while循环了

 try {
            File file = new File("helloworld.txt");
            System.out.println("file.length "+file.length());
            long fileLength  = file.length();
            Source source = Okio.source(file);
            Sink sink = Okio.sink(System.out);
            Buffer buffer = new Buffer();
            while (fileLength!=0) {
                long hasRead = source.read(buffer, file.length());
                fileLength-=hasRead;
            }
            System.out.println("buffer size "+buffer.size());
//            sink.write(buffer, file.length());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

执行输出结果如下

file.length 35243
buffer size 35243

好了,至此Source基本上讲解完毕,接下来讲解Sink,老规矩还是从Okio.sink(OutputStream out,Timeout timeout)讲起

2.2 Okio.sink(final OutputStream out, final Timeout 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() {
    //把Buffer中的数据写入到sink中
      @Override public void write(Buffer source, long byteCount) throws IOException {
      //检查buffer中的数据数量是否合法(如果buffer数量<byteCount就不合法)
        checkOffsetAndCount(source.size, 0, byteCount);
        //自带while循环,直到把buffer中的数据耗尽
        while (byteCount > 0) {
          timeout.throwIfReached();
          //从buffer的第一个Segment开始
          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;
        //如果当前Segment写完了出队列
          if (head.pos == head.limit) {
            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;
      }

      @Override public String toString() {
        return "sink(" + out + ")";
      }
    };

总结下Okio.sink(OutputStream out,Timeout timeout)

  • 检查Buffer的size 和readCount是否合法
  • 获取Buffer中的Head Segment,把Segment中的数据写入到OutputStream,如果当前Segment数据写完了,Segment出队列,并放回对象池
  • 判断数据是否写完,如果没写完,重复第二部

4. BufferedSource BufferedSink源码

image

BufferedSource、BufferedSink 与Source和Sink的区别如下

  • BufferedSource、BufferedSink内部维护了一个Buffer对象
  • BufferedSource、BufferedSink内部分别引用了Source、Sink对象
  • BufferedSource的read(Buffer sink,long byteCount)使用内部的Source的read(Buffer sink,long byteCount)方法
  • BufferedSink的write(Buffer source,long byteCount)使用内部的Sink的write(Buffer source,long byteCount)方法
  • BufferedSource、BufferedSink内部扩展了很多readXX方法如 readByte/writeByte、readInt/writeInt等等

关于BufferedXX系列的源码可能需要再写一篇文章详细讲解。不过也是挺简单的。如果你看懂了本文自行分析BufferedXX应该是不在话下

5. 扩展Source Sink

Okio内部有不少已实现的Source和Sink。例如GzipSource/GzipSink、HashingSource/HashingSink。至于源码分析,请读者自行分析。

6. 接下来要做的事情

Okio是OkHttp IO操作的基石。接下来我们将带着Okio的学习成果进入OkHttp源码分析

美团高级Android面经:OkHttp拦截器链、Okio源码、LeakCanary原理、BlockCanary 美团高级Android面试核心考点解析:聚焦OkHttp、性能监控工具及源码阅读能力。面试重点包括:1)OkHttp拦截器链工作原理(责任链模式+5大默认拦截器执行顺序);2)连接池复用机制(5个空闲连接/5分钟keep-alive);3)Okio的Segment内存池优化;4)LeakCanary检测原理(WeakReference+ReferenceQueue+hprof分析);5)BlockCanary卡顿监控实现(Hook Looper Printer)。 阅读详情

相关推荐

【Okio】Okio 简单入门

1.概述 好文章:https://www.cnblogs.com/leipDao/p/10521844.html 好文章:https://segmentfault.com/a/1190000012705584 好文章:https://www.jianshu.com/p/f033a64539a1 Retrofit,OkHttp,Okio 是 Square 团队开源的安卓平台网络层三板斧,它们逐层分工,非常优雅地解决我们对网络请求甚至更广泛的 I/O 操作的需求。其中最底层的 Okio 堪称小而美,功能也更基.

九师兄 1228

OkHttp解析(三)关于Okio

OkHttp解析(三)关于Okio

QG 1567

从学生项目到工程实践:手把手教你用ATE测试电源芯片的Rdson与漏电流

本文详细介绍了如何将学术级的电源芯片测试方案转化为工业级解决方案,重点讲解使用ATE设备测试Rdson(导通电阻)和漏电流的关键技术。从设备选型、测试夹具设计到量产环境下的工程化实现,提供了实用的优化技巧和常见问题解决方案,帮助工程师提升测试效率和精度。

weixin_42525289的博客 182

Okhttp使用指南与源码分析

原文地址:https://www.jianshu.com/p/a5782d9f24d3使用指南篇#为什么使用okhttp###Android为我们提供了两种HTTP交互的方式:HttpURLConnection 和 Apache HTTP Client,虽然两者都支持HTTPS,流的上传和下载,配置超时,IPv6和连接池,已足够满足我们各种HTTP请求的需求。但更高效的使用HTTP可以让您的应用运...

chenhuakang的博客 886

手把手教你安装 Vivado2022.2(附安装包)

Vivado 2022.2版本与之前的版本相比,具有以下几个显著的优势:电源设计管理器(PDM):Vivado 2022.2引入了全新的电源设计管理器(PDM),这是一个下一代功耗评估平台,为Versal® 和 Kria™ SOM提供了准确一致的功耗估计能力。PDM是Versal产品家族的首选功耗评估工具,为Versal ACAP硬IP块提供增强向导,简化从XPE向PDM的迁移,帮助用户获得准确的功耗评估。

dpwkj的博客 2万+

基于Android平台下的健康管理APP/基于android的健康管理系统

随着网络科技的发展,移动智能终端逐渐走进人们的视线,相关应用越来越广泛,并在人们的日常生活中扮演着越来越重要的角色。因此,关键应用程序的开发成为影响移动智能终端普及的重要因素,设计并开发实用、方便的应用程序具有重要的意义和良好的市场前景。Android技术作为当前最流行的操作平台,自然也存在着大量的应用服务需求。 本课题研究的是基于Android技术平台的健康管理APP,开发这款健康管理APP主要是为了帮助用户可以不用约束时间与地点进行所需信息。本文详细讲述了健康管理APP的界面设计及使用,主要包括界面的

m0_73706453的博客 1439

Android 搜索报错: java.io.InterruptedIOException: thread interrupted ,延迟搜索策略

报错地方: java.io.InterruptedIOException: thread interrupted at okio.Timeout.throwIfReached(Timeout.java:145) at okio.Okio$1.write(Okio.java:76) 我在搜索的时候出现这个问题,一开始以为是rxjava后面请求未解绑的问题,但是实际上,在zip操作的时候已经做了判断 if (null != zipDisposable &&a

kururunga的博客 7948

学习海康VisionMaster之顶点检测

2:应用场景:海康VisionMaster的顶点检测功能在工业视觉检测中具有广泛的应用场景,尤其在精密制造、电子元件检测和机械加工等领域,其通过高精度定位物体的关键点(如角点、交点或边缘顶点)来实现自动化测量与质量控制。基本参数这里就是画ROI,设定跟踪参数和之前的工具是一样的方法,这里画的方向需要注意,如下图所示,边缘的扫描方向正好对应需要查找的边缘点。一个不是很规则的物体需要检测一个凸台的顶角坐标,其实靠边缘与边缘交点是不太稳定的,如下图所示,边缘很短,很容易产生误抓边缘。1:什么是顶点检测?

yuhouxiyang的专栏 1526

OkHttp解析大总结

先来看一下Request这个类,此类还是比较简单的,没有太多复杂的代码,内部就是对一些url、method、body等变量的封装。在整个OkHttp的网络请求流程中有一大部分的代码就是用来对这几个变量进行二次修改。源码如下: 接着再来看Call这个类,在 OkHttp面试之–OkHttp的整个异步请求流程这一节中我们了解到真正的Call对象应该是RealCall类型,那我们就来看一下这个RealC

zxm317122667的专栏 5422

【AI测试必学】DeepSeek API 快速入门:获取 API Key 与调用 API 步骤详解

DeepSeek API 使用与 OpenAI 兼容的格式,可以通过修改配置使用 OpenAI SDK。生成的 API Key 只会显示一次,请妥善保存。注册并登录账号,进入左侧导航栏的。创建 API Key。

blues_C的博客 1万+

1、世界级模拟电路设计指南

本文详细探讨了模拟电路设计的各个方面,从基础概念到实际应用,涵盖了元件选择、滤波器设计、反馈系统、噪声问题以及SPICE仿真的优缺点。文中还提供了设计实例和无计算机设计方法,旨在帮助工程师提升模拟电路设计的效率与可靠性。

s1t2u3的博客 99

Android开发实战宝典,附答案+考点,源码解析

由于本文罗列的知识点是根据我自身总结出来的,并且由于本人水平有限,无法全部提及,欢迎大神们能补充~将来我会对上面的知识点一个一个深入学习,也希望有童鞋跟我一起学习,一起进阶。提升架构认知不是一蹴而就的,它离不开刻意学习和思考。**这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家,**梳理了多年的架构经验,筹备近1个月最新录制的,相信这份视频能给你带来不一样的启发、收获。,第一时间获取最新知识点Android架构师之路很漫长,一起共勉吧!

2401_84009300的博客 796

IDM 的免费替代品来了!这款开源下载器速度提升500%

💡分享一款开源的下载管理工具,支持多线程加速(最高提升500%)、队列管理、计划任务和浏览器集成,界面现代化且支持深色模式,跨平台支持Windows/Linux,是IDM的优秀替代方案🎈

XiaoqiangClub的博客 734

都看OkHttp的源码,Okio的源码你看过吗?

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。当然我也为你们整理好了百度、阿里、腾讯、字节跳动等等互联网超级大厂的历年面试真题集锦。这也是我这些年来养成的习惯,一定要学会把好的东西,归纳整理,然后系统的消化吸收,这样才能极大的提高学习效率和成长进阶。碎片、零散化的东西,我觉得最没有价值的。就好比你给我一张扑克牌,我只会觉得它是一张废纸,但如果你给我一副扑克牌,它便有了它的价值。

2401_83817769的博客 515

黑猫带你学UFS协议第13篇:UFS初始化详解(Boot Operation)

本文详细解析了UFS设备的初始化过程,包括部分初始化、加载启动代码(可选)和初始化完成三个阶段。重点介绍了如何通过设备描述符和逻辑单元描述符配置引导特性,以及如何从UFS设备加载启动代码。内容适用于嵌入式硬件和存储领域的开发者。

黑猫奥利奥的博客 3555

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

都看OkHttp的源码,Okio的源码你看过吗?(1)

Source:Okio对输入流的抽象接口,提供了read方法将数据读到buffer中Sink:Okio对输出流的抽象接口,提供了write方法将数据写到buffer中BufferedSource和BufferedSink:是Okio对Buffer的接口抽象,分别继承Source和SinkRealBufferedSource:BufferedSource实现类,读操作都是由该类来完成,可以通过Okio工厂获取。

2401_83817689的博客 382

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

OkHttp——OKio(7)

文章目录OKio结构图OKio读取文本OKio写入文本总结 OKio结构图 从左半部分的图我们可以看出OKio本质是对Java原生IO对一次封装。 那么,在OKio 的帮助下,完成一次读写操作又是怎样的呢? OKio读取文本 下面是一段按行读取文本的代码,其首先构建一个Source,类似于Java的InputStream,然后构建一个BufferedSource,类似于Java的Buffered...

m0_37884977的博客 591

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

OKhttp中的okio、拦截器、响应

OKhttp中的okio、拦截器、响应 前言:本篇博客将由浅入深地对OKhttp进行解析,包括基本使用、okio、拦截器和响应等内容,和读者一起全面系统地了解OKhttp的知识,深入了解Android客户端网络请求方法及过程,学习OKhttp采用的设计模式和架构。 一、OKhttp概述: HTTP是现代应用常用的一种交换数据和媒体的网络方式,高效地使用HTTP能让资源加载更快,节省带宽。OkHttp是一个高效的HTTP客户端,它有以下默认特性: 支持HTTP/2,允许所有同一个主机地址的请求共享同一

居家小男的博客 1442

OKHttp源码分析---OKIO

简介 JavaNIO和阻塞I/O 阻塞I/O通信模式:调用InputStream.read()方法时是阻塞的,它会一直等到数据来时才返回。 NIO通信模式:在JDK1.4开始,是一种非阻塞I/O,在Java NIO的服务器由一个专门的线程来处理所有I/O事件,并负责分发。线程之间通讯通过wait和notify等方式。 okio okio是由square公司开发的,它补充了java.io和jav...

小薇子的博客 914

OkHttp和Okio

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33968104的博客 1403

Android框架-OkHttp与OKio

1.Okio 1.1 输入与输出 以前总是搞不清输入与输出,是因为处的位置不对。输入与输出是站在程序自身的角度来看的。 1.2 历史 java.io --> java.nio --> okio 1.3 ByteString 1.4 Buffer 2.OkHttp 2.1 简介 OKHttp是由Sq...

云上道人的博客 5766

Okio基本使用以及源码分析

Okio是什么 在OkHttp的源码中经常能看到Okio的身影,所以单独拿出来学习一下,作为OkHttp的低层IO库,Okio确实比传统的java输入输出流读写更加方便高效。Okio补充了java.io和java.nio的不足,使访问、存储和处理数据更加容易,它起初只是作为OKHttp的一个组件,现在你可以独立的使用它来解决一些IO问题。 先看下okio库中类之间的关系: okio中最关键的是对于缓存队列的管理,这些优化操作使得okio在复制数据的时候可以减少拷贝次数,来看下okio中数据保存的数据结构是

u012345683的博客 907

一文详解 Okio 输入输出流

Okio最初是作为OkHttp的一个组件出现,是 OkHttp 实现HTTP协议数据构建、解析中使用到的底层 IO 库。其相比于传统的 java.io 和 java.nio ,其在文件、网络等数据读写操作更加便捷、高效。

bjxiaxueliang的CODING小栈 1250

Okio 深度指南

**Okio** 是一个现代化的 I/O 库,由 Square 公司开发并维护,旨在提供更高效、更易于使用的文件读写、网络流处理等功能。Okio 增强了 Java 的标准 I/O API,使开发者能够更好地控制缓冲区管理和异步任务,特别是在处理大量数据时性能显著提升。它也是 [OkHttp](https://square.github.io/okhttp/) 网络库的基础组件。 ## 2. 项目...

gitblog_00555的博客 807
上一篇: Android Context ContextWrapper ContextImpl Activity Service关系
下一篇: OkHttp源码详解之二完结篇
字节小站
博客等级 码龄10年 53粉丝 55原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值