theme: juejin
highlight: a11y-dark

同步请求

OkHttpClient httpClient = new OkHttpClient();

String url = "https://www.baidu.com/";
Request getRequest = new Request.Builder()
.url(url)
.get()
.build(); Call call = httpClient.newCall(getRequest); new Thread(new Runnable() {
@Override
public void run() {
try {
//同步请求,要放到子线程执行
Response response = call.execute();
Log.i(TAG, "okHttpGet run: response:"+ response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();

总结:

  1. 创建OkHttpClient和Request对象

  2. 将Request封装成Call对象

  3. 调用Call的execute()发送同步请求

同步执行流程

第一步,创建一个OkHttpClient对象

OkHttpClient mClient = new OkHttpClient.Builder().build();
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}

重点关注Dispatcher、ConnectionPool的初始化。Dispatcher和异步请求有关,我们先不讲,我们重要讲一下ConnectionPool。

ConnectionPool有两个作用:

作用一,当你请求的url是相同的时候,就可以复用这个Connection;

作用二,ConnectionPool可以设置哪些Connection保持打开,哪些Connection可以保持复用;

第二步,创建携带请求信息的Request对象

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

Builder(Request request) {
this.url = request.url;
this.method = request.method;
this.body = request.body;
this.tag = request.tag;
this.headers = request.headers.newBuilder();
}

第三步,创建Call对象

@Override public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
} RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
final EventListener.Factory eventListenerFactory = client.eventListenerFactory(); this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
(1)重定向拦截器
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket); // TODO(jwilson): this is unsafe publication and not threadsafe.
this.eventListener = eventListenerFactory.create(this);
}

第四步,call.execute()

@Override public Response execute() throws IOException {
(1)通过标注位一个请求只能执行一次
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
(2)捕捉堆栈信息
captureCallStackTrace();
try {
(3)将请求加入同步队列
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
(4)移除请求
client.dispatcher().finished(this);
}

private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
(1)移除请求
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();
}
}

异步请求

 String url = "https://www.jianshu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.get()//默认就是GET请求,可以不写
.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 {
Log.d(TAG, "onResponse() returned: " + response);
}
});

总结:

  1. 创建OkHttpClient和Request对象

  2. 将Request封装成Call对象

  3. 调用Call的enqueue()发送异步请求

异步请求执行流程

前三步和同步一样,不在分析。

第四步,call.enqueue()

@Override public void enqueue(Callback responseCallback) {
(1)通过标注位一个请求只能执行一次
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) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}

正在异步请求数小于64并且当前url的host请求数小于5就放到正在运行的异步队列,否则放到等待的异步队列。

通过线程池运行AsyncCall。

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;
}
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 {
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 {
(1)重点关注
client.dispatcher().finished(this);
}
}
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
(1)移除当前请求
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
(2)执行等待队列中的请求
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
} if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
} 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.
}
}

总结:

  1. 通过没有核心线程的线程池执行请求

  2. 在finally中正在运行的队列移除请求,并执行等待队列中的可以执行的请求。

参考:

Okhttp请求的更多相关文章

  1. Okhttp 请求流程梳理

    最近在看 Okhttp 的源码.不得不说源码设计的很巧妙,从中能学到很多.其实网上关于 Okhttp 的文章已经很多了,自己也看了很多.但是俗话说得好,好记性不如烂笔头,当你动手的时候,你会发现你在看 ...

  2. 自己封装的OKhttp请求

    package com.create.qdocumentimtest.rxjavatest; import com.squareup.okhttp.Callback; import com.squar ...

  3. Android 中OKHttp请求数据get和post

    1:在Android Studio 的 build.gradle下  添加 然后再同步一下 compile 'com.squareup.okhttp:okhttp:2.4.0'compile 'com ...

  4. 安卓OKhttp请求封装

    目前安卓开发中使用的网络工具为OKhttp,但是okhttp的使用还不是很方便,在okhttp的基础上再对请求进行封装会极大的方便网络调用. 下面直接上代码. 请求封装 public class Ht ...

  5. okhttp请求文件异常解决方法

    微信借口中获取素材的接口返回只是一个缓存的文件流 InputStream, 接口中请求永久图片素材返回的是 InputStream; 接口项目获取到 InputStream 可以直接通过文件流写到文件 ...

  6. okhttp 请求list数据实例

    public class DataBean { /** * id : 61684 * movieName : <猜火车2>先导预告片 * coverImg : http://img31.m ...

  7. Okhttp同步请求源码分析

    进阶android,OKhttp源码分析——同步请求的源码分析 OKhttp是我们经常用到的框架,作为开发者们,我们不单单要学会灵活使用,还要知道他的源码是如何设计的. 今天我们来分析一下OKhttp ...

  8. 安卓中使用OkHttp发送数据请求的两种方式(同、异步的GET、POST) 示例-- Android基础

    1.首先看一下最终效果的截图,看看是不是你想要的,这个年代大家都很忙,开门见山很重要! 简要说下,点击不同按钮可以实现通过不同的方式发送OkHttp请求,并返回数据,这里请求的是网页,所以返回的都是些 ...

  9. java okhttp发送post请求

    java的httpclient和okhttp请求网络,构造一个基本的post get请求,都比py的requests步骤多很多,也比py的自带包urllib麻烦些. 先封装成get post工具类,工 ...

  10. Android 普通okhttp、okhttp utils执行 post get请求,文件上传下载、请求图片

    public class OKHttpActivity extends Activity implements View.OnClickListener { public static final M ...

随机推荐

  1. 蓝桥杯训练赛二-1199 问题 E: 哥德巴赫曾猜测

    题目描述 德国数学家哥德巴赫曾猜测:任何大于6的偶数都可以分解成两个素数(素数对)的和.但有些偶数可以分解成多种素数对的和,如: 10=3+7,10=5+5,即10可以分解成两种不同的素数对 输入 输 ...

  2. DUBBO消费端Reference初始化

    1. ReferenceBean.java ReferenceBean.java 继承 ReferenceConfig.java, 主要的Reference初始化逻辑都在ReferenceBean的g ...

  3. 如何让一个 div 左右各空一定距离然后 div 占满剩下的宽度?

    如何让一个 div 左右各空一定距离然后 div 占满剩下的宽度? 最开始想到的方法就是 div 宽度取 n%, style 设置 margin:auto. 但是这样有一个问题,一定距离是根据 n 的 ...

  4. Typora的初使用

    HelloTypora 二级标题 三级标题 HelloWorld! HelloWorld HelloWorld HelloWorld HelloWorld HelloWorld 引用 分割线 图片 # ...

  5. vi 异常退出出现 E325:Attention的解决办法

    在linux系统下使用vi编辑程序的时候,没有保存退出,直接关闭了,出现了以下的情况: 打开就会显示filename.c.swap已经存在. 这是因为vi在编辑文件时会创建一个交换文件swap fil ...

  6. Es6中模块引入的相关内容

    注意:AMD规范和commonJS规范 1.相同点:都是为了模块化. 2.不同点:AMD规范则是非同步加载模块,允许指定回调函数.CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行 ...

  7. 项目中pom.xml的某些坐标无法加载

    项目中pom.xml的某些坐标无法加载 maven官方查找对应的坐标文件下载 https://mvnrepository.com/artifact/com.fasterxml.jackson.core ...

  8. PHP基础教程(二)

    本部分列出了在 PHP 中使用的各种运算符:算数运算符 运算符 说明 例子 结果 + Addition x=2x+2 4 - Subtraction x=25-x 3 * Multiplication ...

  9. Taro3 扫描不同二维码参数不同,但是热启动之后参数不变 根据环境不同更换域名

    热启动:先执行缓存的静态数据,然后再执行页面代码.比如右上角退出或者按home键错误用法:用的Taro3 react function函数,之前用的  Taro.getLaunchOptionsSyn ...

  10. Application.HandleMessage与Application.ProcessMessage

    HandleMessage: HandleMessage中断应用程序的执行,以便Windows可以在将控制权返回给应用程序之前处理来自Windows消息队列的单个消息. 如果消息队列为空,则Handl ...