Android知名三方库OKHttp(一) - 基本使用源码分析

本文目标

搞明白OKHttp的源码同步请求和异步请求基本流程

基本使用

同步请求

    public void synRequest() {
        //1.创建okHttpClient和创建Request对象
        OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
        //把配置的请求信息封装成Request对象
        Request request = new Request
                .Builder()
                .url("http://www.baidu.com")
                .get()
                .build();
        //2.把Request对象封装成call对象
        Call call = client.newCall(request);
        try {
            //3.发起同步请求
            Response response = call.execute();
            System.out.println(response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

异步请求

    public void asyRequest() {
        //1.创建okHttpClient和创建Request对象
        OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
        Request request = new Request
                .Builder()
                .url("http://www.baidu.com")
                .get().build();
        //2.把Request对象封装成call对象
        Call call = client.newCall(request);
        //3.发起异步请求
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //都在子线程
                System.out.println("Fail");
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //都在子线程
                System.out.println(response.body().string());
            }
        });
    }
  • 1.创建okHttpClient和创建Request对象(配置的请求信息封装)
  • 2.把Request对象封装成call对象
  • 3.发起同步请求或异步请求

1.1OkHttpClient

首先先看OkHttpClient内Builder这个内部类

OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {

  public static final class Builder {

    public Builder() {
      dispatcher = new Dispatcher();//重要,请求的分发器
      ...
      connectionPool = new ConnectionPool();//连接池
      ...
    }

   //最后通过build()方法把配置信息封装成OkHttpClient对象
   public OkHttpClient build() {
      return new OkHttpClient(this);
    }
  }
}

可以看到这里面创建了一大堆对象(这里只是挑了重要的对象),不过不要怕,我们看重要的
dispatcher = new Dispatcher();请求的分发器,同步请求放到队列当中来执行
connectionPool = new ConnectionPool();连接池,客户端和服务器的链接由它来统一管理,如果url相同则会复用,各种策略都在这个类中来管理
最后通过build()方法把配置信息封装成OkHttpClient对象

1.2Request

这是Request对象的构建

Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .get()
                .build();

具体源码如下

public final class Request {

  final HttpUrl url;//链接
  final String method;//请求方式
  final Headers headers;//头信息
  final @Nullable RequestBody body;//请求体
  final Object tag;

  Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tag = builder.tag != null ? builder.tag : this;
  }
   ... 

  //内部类Builder
  public static class Builder {

    HttpUrl url;
    String method;
    Headers.Builder headers;
    RequestBody body;
    Object tag;

    public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }

    public Builder url(HttpUrl url) {
      if (url == null) throw new NullPointerException("url == null");
      this.url = url;
      return this;
    }

    ... 

    public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }
  }
}

把配置的请求信息封装成Request对象,包含url,method请求方式,headers头信息,RequestBody请求体

2.Call

把Request对象封装成call对象

Call call = client.newCall(request);

首先client.newCall(request);调用进去,会发现是RealCall在调用

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }
}

final class RealCall implements Call {

  final OkHttpClient client;
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;

  private EventListener eventListener;

  /** The application's original request unadulterated by redirects or auth headers. */
  final Request originalRequest;
  final boolean forWebSocket;

  // Guarded by this.
  private boolean executed;

  private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);//重定向拦截器
  }

  static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }
}

可以看出来,RealCall是持有了前面创建的OkHttpClient和Request对象

3.1Call的同步请求

      try {
            //3.发起同步请求
            Response response = call.execute();
            System.out.println(response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }

call.execute();追进去

final class RealCall implements Call {

  @Override public Response execute() throws IOException {
    synchronized (this) {
     // 同一个http请求只能请求一次
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    //捕捉堆栈信息
    captureCallStackTrace();
    //开启了监听事件
    eventListener.callStart(this);
    try {
      //添加到执行队列当中
      client.dispatcher().executed(this);
      //拦截器链来返回结果
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      //回收请求
      client.dispatcher().finished(this);
    }
  }
}

上面这个方法最核心的就是,把这个同步请求添加到队列当中了,然后才是进入拦截器链中经过若干个拦截器直到最后返回(后面在分析拦截器)
client.dispatcher().executed(this);这一行追进去是下面的代码

public final class Dispatcher {

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

  /** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }
}

最后还会回收请求 client.dispatcher().finished(this);
追进去看一下

public final class Dispatcher {
  //移除同步请求
  void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
  }

  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();//计算现有的请求
      idleCallback = this.idleCallback;
    }

//没有可运行的请求 &&  idleCallback不为空
    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

  //计算正在执行的请求数量
  public synchronized int runningCallsCount() {
    return runningAsyncCalls.size() + runningSyncCalls.size();
  }
}

主要就是从当前队列中移除这个同步请求,如果不能移除抛出异常,同步的时候promoteCalls这个参数是false,所以执行不到promoteCalls()这个方法,异步可以执行到,然后调用runningCallsCount()会计算现有的请求,最后没有可运行的请求 && idleCallback不为空就会调用 idleCallback.run();方法,至此同步请求流程结束
总结一下同步请求:

  • 保存同步请求
  • 移除同步请求
    无非就是做了这两件事情

3.2Call的异步请求

        //3.发起异步请求
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //都在子线程
                System.out.println("Fail");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //都在子线程
                System.out.println(response.body().string());
            }
        });

我们从call.enqueue()调用这个方法开始看,里面会传入一个Callback对象用来获取网络成功或者失败的回调,然后我们来看enqueue()方法

final class RealCall implements Call {

  @Override 
  public void enqueue(Callback responseCallback) {
    //加锁
    synchronized (this) {
      //call 这个实例有没有执行过,上面得到的call只能被执行一次,否则就抛出异常
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }


//内部类
 final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

    @Override 
    protected void execute() {
      boolean signalledCallback = false;
      try {
        //拦截器链获取响应结果
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }
}

NamedRunnable是继承自Runnable的

public abstract class NamedRunnable implements Runnable {
  ...
}

client.dispatcher().enqueue(new AsyncCall(responseCallback));我们先来看enqueue()这个方法


public final class Dispatcher {

  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;
  private @Nullable Runnable idleCallback;

  //线程池
  private @Nullable ExecutorService executorService;
 //异步等待队列
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
  //异步执行队列
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

 //同步锁
  synchronized void enqueue(AsyncCall call) {
    //如果正在执行的异步队列 < 64 && 正在执行的主机的请求数 < 5 ,则把请求添加到执行队列中,通过线程池去执行这个请求
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }
}

在enqueue这个方法中首先是同步的,然后如果正在执行的异步队列 < 64 && 正在执行的主机的请求数 < 5 ,则把请求添加到执行队列中,通过线程池去执行这个请求
总结一下 call.enqueue(callback)方法

  • 1.判断当前call
  • 2.封装成了一个AsyncCall对象
  • 3.client.dispatch().enqueue()

判断当前call,call 这个实例有没有执行过,call只能被执行一次,否则就抛出异常,然后通过传递进来的callBack对象我们吧它封装成了一个AsyncCall对象(其实就是个runnable),然后就会调用client.dispatch().enqueue()方法进行异步请求,同时如果正在执行的异步队列 < 64 && 正在执行的主机的请求数 < 5 ,则把请求添加到执行队列中,通过线程池去执行这个请求,如不不是的话就添加到异步等待队列中

接下来我们来看下线程池

public final class Dispatcher {
  private @Nullable ExecutorService executorService;

 //同步并保证线程池单例
  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
}

该线程池最大线程数量为Integer.MAX_VALUE,但是不要担心,在之前限制了 maxRequests=64,就是说最多同时只有64个网络请求,然后该线程池内的线程只能存活60秒,其实该线程池执行的runnable对象是AsyncCall对象,来让我们再次看下这个AsyncCall对象

  final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    RealCall get() {
      return RealCall.this;
    }

    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        //重定向拦截器是否取消了
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          //如果没有取消则调用onResponse()通过回调接口把response返回出去
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

我们发现该AsyncCall是继承自NamedRunnable,来看下

public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

发现NamedRunnable继承自Runnable,那也就是说AsyncCall是个runnable,那我们看下run()方法,发现AsyncCall类中没有,那就看父类NamedRunnable的,发现最后调用了execute()方法,那我们可以看AsyncCall的execute()方法,在这里有一行Response response = getResponseWithInterceptorChain();,这才是真正获取响应结果的代码,这是拦截器链(我们后面会分析),然后通过回调接口把数据返出去,在finally中我们又看到了client.dispatcher().finished(this);

public final class Dispatcher {
 //异步请求的回收,这次第3个参数为true
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }
}

该finished方法共做了3件事:

  • 1.把这个请求在正在请求的异步队列中移除掉,就是这行代码calls.remove(call)
    1. promoteCalls(); 调整整个异步请求的队列
  • 3.重新计算正在运行的往前请求数量并进行赋值

问题1:OKIO是什么?
特点
它也是基于插管的,⽽且是单向的,输⼊源叫 Source,输出⽬标叫Sink。
⽀持 Buffer,和 NIO ⼀样,可以对 Buffer 进⾏操作,但不强制使⽤Buffer。
⽤法

BufferedSource source = Okio.buffer(Okio.source(new
File("./io/text.txt"))));
System.out.println(source.readUtf8Line());

问题2:OKIO 与 IO、NIO 区别
okio 相⽐ io 和 nio,api 更简单易⽤。
⽀持超时机制。
引⼊ ByteString ⽤空间换时间提⾼性能。
采⽤ segment 机制进⾏内存共享,节省复制时间消耗。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容