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. 一些开源软件的LOGO

    整理一些开源软件的logo或者吉祥物,主要是一些以动物形象为主的logo. 1. GNU,不是一个软件,而是一个软件组织,包括很多知名的软件例如GCC编译器. GNU的LOGO是一只牛. GCC的lo ...

  2. 超2T硬盘使用gpt分区及做成lvm

    1.超过2T分区不能用fdisk了,用parted 分区格式化后对新的分区做lvm

  3. 关于 echarts 使用 geo 制作地图 tooltip 不显示问题(转)

    原文地址 我之前遇到过这问题,单独设置 tooltip 没效果,geo 下面也有 tooltip 属性,但是也不管用,网上查了一下说 geo 不支持 tooltip 提示框显示,就自己根据 echar ...

  4. ASP.NET Core Web API通过中间件或UseExceptionHandler异常处理方法

    UseExceptionHandler app.UseExceptionHandler(configure => { configure.Run(async context => { va ...

  5. 串口USART(续二)

    通过前面的分析知道,在LPC824中,USART所拥有的配置寄存器有很多,但在一般情况下,如果只是简单地使用收发功能,则只需要使用到配置寄存器CFG.波特率发生器寄存器BRG.中断使能读取和置位寄存器 ...

  6. std::string实现split和trim方法

    void split(const std::string& str, const std::string& strDelimiter, std::vector<std::stri ...

  7. spring cloud 配置文件加密解密

    1.底包 <dependency>   <groupId>org.springframework.security</groupId>   <artifact ...

  8. 学习lua-03,集合排序,集合插入元素

    array = {"Google", "Runoob"} table.insert(array,1,"hello world!") for ...

  9. Linux 库的使用

    Linux 库的使用 -I头文件的路径-L动态库的路径   命名 使用 静态库 lib名字.a 静态库路径/lib名字.a 动态库 lib名字.so -L动态库路径 -l名字 编译 #静态编译 # g ...

  10. dota中的哲理

    战术和战略: 6k分和3k分玩家的最重要的区别不是英雄玩的不好,而是整体战略不明确. dota玩家游戏时长超过1000h的比比皆是,这些玩家里面分数差距相当大.高的7k往上,低的2k深坑爬不出来. 这 ...