《OkHttp源码分析》之 OkHttp请求流程分析

《OkHttp源码分析》之 OkHttp请求流程分析_第1张图片

本文作者

《OkHttp源码分析》之 OkHttp请求流程分析_第2张图片

09

1-2018

Mirs_sir

博客:http://my.csdn.net/a109340

声明:本文由作者 Mirs_sir 授权发布,未经原作者允许请勿转载

 看到上面蓝色字了吗,点下吧

0?wx_fmt=gif&wxfrom=5&wx_lazy=1

感谢 Mirs_sir 为我们带来OkHttp源码分析系列文章,目录如下:


  • 1.Http请求原理

  • 2.OkHttp的简单使用

  • 3.OkHttp的初始化

  • 4.OkHttp请求流程分析

  • 5.OkHttp的请求拦截链

  • 6.OkHttp请求调度的分析

  • 7.OkHttp的缓存管理

  • 8.深入源码理解HashMap、LinkedHashMap,DiskLruCache


日更一篇,敬请期待哦


小编温馨提示:代码块向右滑动可查看更多代码,长按可复制代码哟

0?wx_fmt=gif&wxfrom=5&wx_lazy=1

OkHttp请求流程分析

OkHttp request process Analysis


《OkHttp源码分析》之 OkHttp请求流程分析_第3张图片

Call和RealCall

《OkHttp源码分析》之 OkHttp请求流程分析_第4张图片


经过上面的初始化之后okhttpClient调用public Call newCall(Request request)方法去构建一个Call


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

可以看到真实的Call是RealCall,得到知识点一般来说,一个组织或者个人的代码风格是差不多的 这里面Call的实现类是RealCall,其他的应该也是Real开头的。


这里我们发现他把OkHttpClient和Reques传了过去,他的构造方法是


RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) { 
final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
//重试和跟进拦截器
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
// TODO(jwilson): this is unsafe publication and not threadsafe.  
// 这是不安全的发布,不是线程安全的。
this.eventListener = eventListenerFactory.create(this);
}


这里面比较面生的是的是RetryAndFollowUpInterceptor按字面意思重试和跟进拦截器进去大概看一下


/**
* This interceptor recovers from failures and follows redirects as necessary. It may throw an
* {@link IOException} if the call was canceled.
* 这个拦截器从故障中恢复,并根据需要遵循重定向。如果呼叫被取消,它可能会抛出IOException。
*/

public final class RetryAndFollowUpInterceptor implements Interceptor {
 /**
  * How many redirects and auth challenges should we attempt? Chrome follows 21 redirects; Firefox,
  * curl, and wget follow 20; Safari follows 16; and HTTP/1.0 recommends 5.
  * 我们应该尝试多少次重定向和认证挑战? Chrome遵循21次重定向; Firefox,curl和wget遵循20; Safari遵循16; HTTP / 1.0建议5。
  */

 private static final int MAX_FOLLOW_UPS = 20;
 private final OkHttpClient client;
 private final boolean forWebSocket;
 private StreamAllocation streamAllocation;
 private Object callStackTrace;
 private volatile boolean canceled;
 public RetryAndFollowUpInterceptor(OkHttpClient client, boolean forWebSocket) {
   this.client = client;
   this.forWebSocket = forWebSocket;
 }
 (....此处省略以后的代码)


果然和字面意思一样,这面看参数的话最大支持20次的重定向。后面的暂时不需要看等用到的时候再看也不迟,避免陷入之间树木不见森林的坑


汉语中类似的后面用工厂方法创建³³了一个EventListener的类,看字面意思就是时间的监听类看里面的方法


Factory
fetchStart
dnsStart
dnsEnd
connectStart
secureConnectStart
secureConnectEnd
connectEnd
requestHeadersStart
requestHeadersEnd
requestBodyStart
requestBodyEnd
responseHeadersStart
responseHeadersEnd
responseBodyStart
responseBodyEnd
fetchEnd


通过这个事件我们大致能看出来OkHttp请求的流程,然后回到RealCall,之后调用的是一个执行或者是enqueue我们在Android项目里由于主线程是不允许有网络请求的,所以我们先来搞enqueue,话不多说,进去看


@Override 
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
//捕获呼叫堆栈跟踪
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}


Already Executed 这个异常大家在平时用的时候应该偶尔会碰到,原因看到了吧。当你的这个请求应该在运行的时候你在去调用的时候就异常了


然后第二个是捕获呼叫堆栈跟踪器,这个就忽略掉,看真正的重头戏


前方高能预警,提起精神看


《OkHttp源码分析》之 OkHttp请求流程分析_第5张图片

Dispatcher

《OkHttp源码分析》之 OkHttp请求流程分析_第6张图片


调用的OkHttpClient的调度程序的排队方法,调度程序的初始化是在OkHttpClient的Builder里面看上面的代码是直接new了一个Dispatcher,我们去调度程序里看


public final class Dispatcher { 
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private Runnable idleCallback;
/* Executes calls. Created lazily. /
private ExecutorService executorService;
/* Ready async calls in the order they’ll be run. 按照他们将要运行的顺序进行准备就绪的异步调用 /
private final Deque readyAsyncCalls = new ArrayDeque<>();
/* Running asynchronous calls. Includes canceled calls that haven’t finished yet. 运行异步调用包括尚未完成的取消请求/
private final Deque runningAsyncCalls = new ArrayDeque<>();
/* Running synchronous calls. Includes canceled calls that haven’t finished yet. 运行同步调用包括尚未完成的取消呼叫/
private final Deque runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
(….此处省略N行代码)
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
}


我们看到他的构造函数就是一个空的然后全局变量的话有个线程池,和三个双端队列有可能有同学不知道Deque是什么,deque即双端队列。是一种具有队列和栈的性质的数据结构。双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。


我们看到排队里面就是操作运行异步调用包括尚未完成的取消请求的runningAsyncCalls,他的判断条件是:


1.当前队列里面的请求数量小于最大请求数也就是64

2.当前队列里面的链接的总主机数量小于最大请求主机数


如果条件成立就添加到这个队列里面,否则的话就添加到readyAsyncCalls里,也就是按照他们将要运行的顺序进行准备就绪的异步调用的队列


加入到运行队列里后,执行executorService().execute(call);方法这个方法就是个新出了一个线程池,然后执行


public synchronized ExecutorService executorService() { 
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}


可能关于线程池的一些东西大家不是特别清楚这里稍微解释一下,首先是他的构造函数


ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, 
BlockingQueue workQueue, RejectedExecutionHandler handler
)
corePoolSize: 线程池维护线程的最少数量
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime: 线程池维护线程所允许的空闲时间
unit: 线程池维护线程所允许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列
handler: 线程池对拒绝任务的处理策略


上面的的SynchronousQueue可能一般同学看的不是特别熟悉这里解释一下:


SynchronousQueue:https://docs.oracle.com/javase/6/docs/api/java/util/concurrent/SynchronousQueue.html

是一个没有数据缓冲的BlockingQueuehttps://docs.oracle.com/javase/6/docs/api/java/util/concurrent/BlockingQueue.html

生产者线程对其插入操作放置操作必须等待消费者的移除操作take,反过来也一样。


SynchronousQueue的一个使用场景的典型就是在线程池里.Executors.newCachedThreadPool()就使用了的SynchronousQueue,这个线程池根据需要(新任务到来时)创建新的线程,如果有空闲线程则会重复使用,线程空闲了60秒后会被回收。执行是调用执行方法。


峰回路转,回到ReallCall的排队里面


这里执行的正式AsyncCall,new AsyncCall(responseCallback)AsyncCalls是RealCall的一个内部类,继承NamedRunnable,NamedRunnable是一个实现了Runnable接口的抽象类,


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


这里他做了两件事

  1. 给当前线程设设置了个名字

  2. 新增了一个抽象方法执行


把握住这两个再来看AsyncCall


《OkHttp源码分析》之 OkHttp请求流程分析_第7张图片

AsyncCall

《OkHttp源码分析》之 OkHttp请求流程分析_第8张图片
final class AsyncCall extends NamedRunnable { 
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
 //命名规则 OkHttp+协议名+域名
 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 {
     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 {
     responseCallback.onFailure(RealCall.this, e);
   }
 } finally {
   //调用dispatcer的finshed方法,(重要,在下文的请求调度分析会将)
   client.dispatcher().finished(this);
 }
}
}


上面比较重要的是这一句


Response response = getResponseWithInterceptorChain(); 


如果重试和跟进拦截器没有被取消的话,返回请求成功调用responseCallback.onResponse,如果中间有什么异常的话调用responseCallback.onFailure(RealCall.this,e);


getResponseWithInterceptorChain() 这个方法非常重要,是整个OkHttp请求的核心,他是组装了一系列的拦截链,进行链式调用,最后返回组装的请求结果


/*一共五个拦截器 包括 
* RetryAndFollowUpInterceptor 重试和跟进拦截器
* BridgeInterceptor 桥拦截器
* CacheInterceptor 缓存拦截器
* ConnectInterceptor 链接拦截器
* CallServerInterceptor 呼叫服务拦截器
*
* RealInterceptorChain 实际拦截链
* */

Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
//建立一个完整的拦截器堆栈。
List interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(
   interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}


接下来我们来走进这些请求链的世界,去分析整个OkHttp请求的具体,我们明天的文章再介绍。



《OkHttp源码分析》之 OkHttp请求流程分析_第9张图片

刘某人程序员

个人微信:Android_LiuGuiLin

新浪微博:@刘某人程序员

《OkHttp源码分析》之 OkHttp请求流程分析_第10张图片

看看我的分享

  长按二维码关注

《OkHttp源码分析》之 OkHttp请求流程分析_第11张图片


你可能感兴趣的:(《OkHttp源码分析》之 OkHttp请求流程分析)