Okhttp系列文章——Okhttp是如何创建请求、响应一个请求

Okhttp系列文章目录


第一章 Okhttp是如何创建请求、响应一个请求

第二章 Okhttp中的设计模式及其应用场景、优缺点

第三章 Okhttp中的数据结构

第四章 Okhttp中的其他相关


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

Okhttp系列文章目录

文章目录

前言

一、Okhttp是如何创建请求?

1、创建同步请求

2、创建异步请求:

二、Okhttp如何响应一个请求——源码分析

1.同步请求

2.异步请求

总结


前言

提示:这里可以添加本文要记录的大概内容:
项目中经常使用OkHttp+retrofit+rxjava进行网络框架的搭建,而Okhttp详细的运行流程及源码却基本很少分析,本文将从基础不同角度去分析Okhttp中的知识。


提示:以下是本篇文章正文内容,下面案例可供参考

一、Okhttp是如何创建请求?

OkHttp官方地址:

https://github.com/square/okhttp

引入库:

implementation("com.squareup.okhttp3:okhttp:4.9.0")

现在的版本都是基于kotlin的,如果习惯java的同学可用低版本查看java版本的源码,本文也是基于java版本源码分析的。

implementation 'com.squareup.okhttp3:okhttp:3.9.0'

1、创建同步请求

 copy官方代码:

OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  try (Response response = client.newCall(request).execute()) {
    return response.body().string();
  }
}

从各种技术文章看到的写入,大概如下:

 OkHttpClient okHttpClient  = new OkHttpClient.Builder()
         .readTimeout(5000, TimeUnit.MILLISECONDS)
         .addInterceptor(new HttpLoggingInterceptor()) //log拦截器,暂时不用管
         .connectTimeout(15,TimeUnit.SECONDS)
         .build();
 Request request = new Request.
         Builder().
         url("http://m.baidu.com").
         addHeader("","").
         build();
Call call = okHttpClient.newCall(request);
 try {
     Response response = call.execute();
 } catch (IOException e) {
     e.printStackTrace();
 }

2、创建异步请求:

 OkHttpClient okHttpClient  = new OkHttpClient.Builder()
         .readTimeout(5000, TimeUnit.MILLISECONDS)
         .addInterceptor(new HttpLoggingInterceptor())//log拦截器,暂时不用管
         .connectTimeout(15,TimeUnit.SECONDS)
         .build();
 Request request = new Request.
         Builder().
         url("http://m.baidu.com").
         addHeader("","").
         build();
Call call = okHttpClient.newCall(request);
 call.enqueue(new Callback() {
     @Override
     public void onFailure(Call call, IOException e) {

     }

     @Override
     public void onResponse(Call call, Response response) throws IOException {

     }
 });

上述可以看到 同步和异步的区别:

//同步请求  

call.execute() ;

//异步请求

call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {

    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {

    }
});

 

二、Okhttp如何响应一个请求——源码分析

1.同步请求

 从call.execute() 分析,查看源码,发现是个接口:

package okhttp3;

import java.io.IOException;

public interface Call extends Cloneable {
  /**
   * @throws IOException if the request could not be executed due to cancellation, a connectivity
   * problem or timeout. Because networks can fail during an exchange, it is possible that the
   * remote server accepted the request before the failure.
   * @throws IllegalStateException when the call has already been executed.
   */
  Response execute() throws IOException;

  /**
   * @throws IllegalStateException when the call has already been executed.
   */
  void enqueue(Callback responseCallback);

  /** Cancels the request, if possible. Requests that are already complete cannot be canceled. */
  void cancel();

  /**
   * Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain
   * #enqueue(Callback) enqueued}. It is an error to execute a call more than once.
   */
  boolean isExecuted();

  boolean isCanceled();

  /**
   * Create a new, identical call to this one which can be enqueued or executed even if this call
   * has already been.
   */
  Call clone();

  interface Factory {
    Call newCall(Request request);
  }
}

既然是接口,那肯定有实现,代码往前定位,发现返回的是RealCall类:

okHttpClient.newCall(request)
@Override public Call newCall(Request request) {
  return RealCall.newRealCall(this, request, false /* for web socket */);
}

现在在RealCall中分析处理call.execute()了,看源码;

@Override public Response execute() throws IOException {
  synchronized (this) {
    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);

/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
  runningSyncCalls.add(call); //队列中加入请求,后续详解
}

1.通过分发器将请求分发处理——Dispatcher分发器,有没有想到事件分发?

2.分发器怎么来的?没看到有定义?——client.dispatcher()这句看出是在OkhttpClient创建时创建的,具体怎么创建的,后续分析OkhttpClient中的设计模式(构建者模式builder)会提到。

继续分析 Dispatcher,看到我们获取的Response 已经得到了。

Response result = getResponseWithInterceptorChain(); //拦截器相关处理,后续设计模式中会分析,暂时不用管
if (result == null) throw new IOException("Canceled");
return result; //返回结果

2.异步请求

从这句出发call.enqueue(new Callback()),why? 这里是最终执行请求的地方,从这里切入是最好的。相关RealCall的流程和上面的同步请求一样,省略。重点分析异步请求:

@Override public void enqueue(Callback responseCallback) {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  captureCallStackTrace();
  eventListener.callStart(this);
  client.dispatcher().enqueue(new AsyncCall(responseCallback)); //重点这句
}

client.dispatcher().enqueue(new AsyncCall(responseCallback)); 也是调用Dispatcher进行分发,继续跟入Dispatcher中相关代码:

synchronized void enqueue(AsyncCall call) {
//相关条件判断:满足即进入运行队列,并运行,否则 进入等待序列
// 条件:maxRequests  =64 最大请求数,maxRequestsPerHost =5 最大同一host请求数
  if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    runningAsyncCalls.add(call); //请求加入运行队列
    executorService().execute(call);// 运行线程池,开始请求
  } else {
    readyAsyncCalls.add(call); //加入等待序列
  }
}

这里AsyncCall 显然是一个Runnable,继承 NamedRunnable,一个带命名的Runnable。感兴趣可以去查看源码。

至此这个同步、异步分析已完成,相关差异也是可以从代码中体现。


总结

提示:这里对文章进行总结:
这里只是对OkHttp的请求过程进行简单的分析,并未深入插入其他知识点,相关详细的分知识点会在其他章节讲解。

附上 Dispatcher相关源码:

/*
 * Copyright (C) 2013 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package okhttp3;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import okhttp3.RealCall.AsyncCall;
import okhttp3.internal.Util;

/**
 * Policy on when async requests are executed.
 *
 * <p>Each dispatcher uses an {@link ExecutorService} to run calls internally. If you supply your
 * own executor, it should be able to run {@linkplain #getMaxRequests the configured maximum} number
 * of calls concurrently.
 */
public final class Dispatcher {
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;
  private @Nullable Runnable idleCallback;

  /** Executes calls. Created lazily. */
  private @Nullable ExecutorService executorService;

  /** Ready async calls in the order they'll be run. */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

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

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

  public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

  public Dispatcher() {
  }

  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;
  }

  /**
   * Set the maximum number of requests to execute concurrently. Above this requests queue in
   * memory, waiting for the running calls to complete.
   *
   * <p>If more than {@code maxRequests} requests are in flight when this is invoked, those requests
   * will remain in flight.
   */
  public synchronized void setMaxRequests(int maxRequests) {
    if (maxRequests < 1) {
      throw new IllegalArgumentException("max < 1: " + maxRequests);
    }
    this.maxRequests = maxRequests;
    promoteCalls();
  }

  public synchronized int getMaxRequests() {
    return maxRequests;
  }

  /**
   * Set the maximum number of requests for each host to execute concurrently. This limits requests
   * by the URL's host name. Note that concurrent requests to a single IP address may still exceed
   * this limit: multiple hostnames may share an IP address or be routed through the same HTTP
   * proxy.
   *
   * <p>If more than {@code maxRequestsPerHost} requests are in flight when this is invoked, those
   * requests will remain in flight.
   */
  public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) {
    if (maxRequestsPerHost < 1) {
      throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost);
    }
    this.maxRequestsPerHost = maxRequestsPerHost;
    promoteCalls();
  }

  public synchronized int getMaxRequestsPerHost() {
    return maxRequestsPerHost;
  }

  /**
   * Set a callback to be invoked each time the dispatcher becomes idle (when the number of running
   * calls returns to zero).
   *
   * <p>Note: The time at which a {@linkplain Call call} is considered idle is different depending
   * on whether it was run {@linkplain Call#enqueue(Callback) asynchronously} or
   * {@linkplain Call#execute() synchronously}. Asynchronous calls become idle after the
   * {@link Callback#onResponse onResponse} or {@link Callback#onFailure onFailure} callback has
   * returned. Synchronous calls become idle once {@link Call#execute() execute()} returns. This
   * means that if you are doing synchronous calls the network layer will not truly be idle until
   * every returned {@link Response} has been closed.
   */
  public synchronized void setIdleCallback(@Nullable Runnable idleCallback) {
    this.idleCallback = idleCallback;
  }

  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

  /**
   * Cancel all calls currently enqueued or executing. Includes calls executed both {@linkplain
   * Call#execute() synchronously} and {@linkplain Call#enqueue asynchronously}.
   */
  public synchronized void cancelAll() {
    for (AsyncCall call : readyAsyncCalls) {
      call.get().cancel();
    }

    for (AsyncCall call : runningAsyncCalls) {
      call.get().cancel();
    }

    for (RealCall call : runningSyncCalls) {
      call.cancel();
    }
  }

  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

  /** Returns the number of running calls that share a host with {@code call}. */
  private int runningCallsForHost(AsyncCall call) {
    int result = 0;
    for (AsyncCall c : runningAsyncCalls) {
      if (c.host().equals(call.host())) result++;
    }
    return result;
  }

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

  /** Used by {@code AsyncCall#run} to signal completion. */
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

  /** Used by {@code Call#execute} to signal completion. */
  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;
    }

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

  /** Returns a snapshot of the calls currently awaiting execution. */
  public synchronized List<Call> queuedCalls() {
    List<Call> result = new ArrayList<>();
    for (AsyncCall asyncCall : readyAsyncCalls) {
      result.add(asyncCall.get());
    }
    return Collections.unmodifiableList(result);
  }

  /** Returns a snapshot of the calls currently being executed. */
  public synchronized List<Call> runningCalls() {
    List<Call> result = new ArrayList<>();
    result.addAll(runningSyncCalls);
    for (AsyncCall asyncCall : runningAsyncCalls) {
      result.add(asyncCall.get());
    }
    return Collections.unmodifiableList(result);
  }

  public synchronized int queuedCallsCount() {
    return readyAsyncCalls.size();
  }

  public synchronized int runningCallsCount() {
    return runningAsyncCalls.size() + runningSyncCalls.size();
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值