OkHttp解析
今天花了一天时间研究了下OkHttp3的内部原理,记录在此处以便后期查阅

我们先来看下基本的使用方式:
public void sendHttpRequest(String url,Callback callback){
OkHttpClient client=new OkHttpClient();
Request request=new Request.Builder().url(url).build();
client.newCall(request).enqueue(callback); //异步执行
try {
client.newCall(request).execute();//同步执行
} catch (IOException e) {
e.printStackTrace();
}
}
接下来的分析就以异步执行开始
@Override
public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
RealCall的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));
}
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {//同时发起的请求不能超过最大请求数,如果超过了,就放入readyAsyncCalls队列
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
线程池
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;
}
执行AsyncCall 的run方法 ,在父类 NamedRunnable 中
@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();//由子类实现
@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 {
client.dispatcher().finished(this);
}
}
现在我们进入 getResponseWithInterceptorChain() 方法查看
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> 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);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
Connection connection) throws IOException {
//........
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next); //拦截器中都会调用 chain.proceed(request)来获取response,产生递归,依次调用各个拦截器的intercept方法 //......... return response;
}
其他拦截器就不看了,我们来看看比较重要的缓存拦截器
@Override public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;//查看是否有缓存
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();//根据缓存响应头返回的一些信息来制定缓存策略,是从缓存取还是从网络上取
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
//..............
// If we're forbidden from using the network and the cache is insufficient, fail. 禁止使用网络并且缓存也是空的,请求失败
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// If we don't need the network, we're done. 如果不需要网络,直接返回缓存
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.update(cacheResponse, response); //更新缓存
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (HttpHeaders.hasBody(response)) {
CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
response = cacheWritingResponse(cacheRequest, response);
}
return response;
}
太晕了,来张图看看吧

OkHttp解析的更多相关文章
- # Okhttp解析—Interceptor详解
Okhttp解析-Interceptor详解 Interceptor可以说是okhttp的精髓之一,Okhttp重写请求/响应.重试.缓存响应等操作,基本都是在各个Interceptor中完成的,上篇 ...
- Okhttp解析—Okhttp概览
Okhttp解析-Okhttp概览 Okhttp作为目前Android使用最为广泛的网络框架之一,我们有必要去深入了解一下,本文是Okhttp解析的第一篇,主要是从宏观上认识Okhttp整个架构是如何 ...
- Android使用OKHTTP解析JSON数据
为了代码重用,我们首先封装一个类.这个类是HttpUtil HttpUtil.java package com.example.asus.networktest; import okhttp3.OkH ...
- okhttp 解析respone:
android,retrofit,okhttp,日志拦截器,使用拦截器Interceptor统一打印请求与响应的json: https://blog.csdn.net/qq_37043246/arti ...
- java.lang.IllegalArgumentException: Illegal character in query at index 261
在BaseFragment中使用了LoadingPage,而LoadingPage的联网加载使用的是AsyncHttpClient.一直报java.lang.IllegalArgumentExcept ...
- Android + HTML5 混合开发
摘要: 对于 Android + HTML5 混合开发以下的观点仅仅是我的个人观点,如果有什么不对的地方请指正 简介: 混合开发的 App(Android + HTML5)就是在一个 App 中内嵌一 ...
- MaterialDesign学习项目
概述 该项目主要用来学习Material Design Support Library和一些android其他技术,也借鉴了网上一些其他优秀的学习资源.该项目目前主要分为俩大部分(后期可能会有一些增加 ...
- Android的路接下来该怎么走?
其实想写这篇文章好久了,很多小伙伴们也经常在群里探讨android移动开发者的走向,一部分人都想多快好省,间歇性踌躇满志.持续性混吃等死 ,只想用CV的开发模式们快速完成工作,然后回家王者农药.其实这 ...
- XRecyclerView上拉刷新下拉加载
效果图: 首先要添加依赖: //xrecyclerviewimplementation 'com.jcodecraeer:xrecyclerview:1.3.2'//Gsonimplementatio ...
随机推荐
- LeetCode ||& Word Break && Word Break II(转)——动态规划
一. Given a string s and a dictionary of words dict, determine if s can be segmented into a space-sep ...
- Java学习之集合
1.ArrayList:采用数组的形式保存对象,这种方式将对象保存在连续的位置中,所以查询效率比较高,但是插入删除时麻烦,并且ArrayList不是线程安全的. 2.Vector:保存对象的方式与Ar ...
- POJ 1151 HDU 1542 Atlantis(扫描线)
题目大意就是:去一个地方探险,然后给你一些地图描写叙述这个地方,每一个描写叙述是一个矩形的右下角和左上角.地图有些地方是重叠的.所以让你求出被描写叙述的地方的总面积. 扫描线的第一道题,想了又想,啸爷 ...
- oracle user locke
1:管理员登录 sqlplus sys/pwd as sysdba sql->alter user jd account unlock; commit; SQL> password new ...
- strsep strpbrk
#include <stdio.h> #include <string.h> int main(void) { char s[] = "aa,bb,cc.11,22, ...
- UltraEdit中使用正则表达式-简单用法
UltraEdit中使用正则表达式 1.认识正则表达式语法: 正则表达式 (UltraEdit Syntax): % 匹配行首 - 表明要搜索的字符串一定在行首. $ 匹配行尾 - 表明要搜索的字符串 ...
- MySQL优化之——触发器
转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46763665 触发器是一个特殊的存储过程,不同的是存储过程要用CALL来调用,而触 ...
- 教你如何配置Ubuntu用于高效、高质量的发送邮件
本文首发在: http://mengxi.me/how-to-setup-ubuntu-sendmail-to-deliver-email-fast-and-reliable/ 在网站上线后,经常会遇 ...
- redis13-----配置文件
==配置文件全解=== ==基本配置 daemonize no 是否以后台进程启动 databases 创建database的数量(默认选中的是database ) #刷新快照到硬盘中,必须满足两者要 ...
- centos 网络连接查看
安装iftop. 这个得先安装epel #yum install epel-replease -y #yum install iftop -y #iftop