OkHttp使用详解及使用连接池的OkHttpUtils工具类

在现代应用程序中,HTTP是用于联网的主要方式。通过高效地使用HTTP,可以使内容加载更快,并节省带宽。

OkHttp是一个高效的HTTP客户端。默认情况下,它具有以下特点:

* 支持HTTP/2,允许对同一主机的所有请求共享一个套接字。

* 提供连接池功能,以减少请求延迟(如果HTTP/2不可用)。

* 能够透明地进行GZIP压缩下载,从而减小文件大小。

* 完全利用响应缓存,避免重复请求网络资源。

* 当网络出现问题时,OkHttp会自动恢复。例如,如果服务有多个IP地址,且第一次连接失败,OkHttp将尝试替代地址。对于托管在冗余数据中心的业务来说,这非常有用。同时,OkHttp支持现代TLS特性(如TLS 1.3、ALPN和证书固定),并且可以配置为后退以获得广泛连接。

使用OkHttp非常简单。其请求/响应API采用流畅的构建器和不可变设计。OkHttp既支持同步阻塞调用,也支持带回调的异步调用。

以下是官方项目地址:Overview - OkHttp (square.github.io)

请求方法详解

我们编写了一些示例代码,演示如何使用OkHttp解决常见问题。请仔细阅读这些示例,了解它们是如何协同工作的。您可以根据需要自由剪切和粘贴这些示例;这就是它们的目的。

## Get请求示例

```java

import okhttp3.OkHttpClient;

import okhttp3.Request;

import okhttp3.Response;

public class Main {

public static void main(String[] args) throws Exception {

OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()

.url("https://api.github.com/users/octocat")

.build();

Response response = client.newCall(request).execute();

System.out.println(response.body().string());

}

}

```

此示例中,我们创建了一个GET请求,访问了GitHub上Octocat用户的信息。运行这段代码后,您将在控制台看到返回的用户信息。

解:根据题意,可以得到以下结论:

(1) 点A在直线$y=-x+3$上,设$A(a,-a+3)$,则有$-a+3=2a$,解得$a=1$,所以$A(1,2)$。

(2) 点B在直线$y=x+1$上,设$B(b,b+1)$,则有$b+1=b+1$,此方程无解,所以点B不存在。

以下是重构后的代码:

```java

import java.io.IOException;

import okhttp3.MediaType;

import okhttp3.OkHttpClient;

import okhttp3.Request;

import okhttp3.RequestBody;

import okhttp3.Response;

public class PostExample {

public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public String post(String url, String json) throws IOException {

RequestBody body = RequestBody.create(json, JSON);

Request request = new Request.Builder()

.url(url)

.post(body)

.build();

try (Response response = client.newCall(request).execute()) {

return response.body().string();

}

}

private String bowlingJson(String player1, String player2) {

return "{\"winCondition\":\"HIGH_SCORE\",\"name\":\"Bowling\",\"round\":4,\"lastSaved\":1367702411696,\"dateStarted\":1367702378785,\"players\":[{\"name\":\"" + player1 + "\",\"history\":[10,8,6,7,8],\"color\":-13388315,\"total\":39},{\"name\":\"" + player2 + "\",\"history\":[6,10,5,10,10],\"color\":-48060,\"total\":41}]}";

}

public static void main(String[] args) throws IOException {

PostExample example = new PostExample();

String json = example.bowlingJson("Jesse", "Jake");

String response = example.post("http://www.roundsapp.com/post", json);

System.out.println(response);

}

}

```

主要重构了以下内容:

将 `post` 方法中的字符串拼接替换为使用占位符的方式,以提高代码可读性和可维护性。

Asynchronous Get with enqueueCallback

在OkHttp中,我们可以使用enqueueCallback方法来实现异步的GET请求。这种方法允许我们在工作线程上下载文件,而不需要等待响应可读。回调函数将在响应标头准备好之后执行,但读取响应体仍然可能阻塞。需要注意的是,OkHttp目前还没有提供异步API来接收响应体。

## 代码示例

首先,我们需要创建一个`Request.Builder`对象,并设置请求的URL和HTTP方法。然后,我们使用`enqueueCallback`方法将请求添加到请求队列中。最后,我们定义回调函数,该函数将在请求完成时被调用。

```java

import okhttp3.Callback;

import okhttp3.OkHttpClient;

import okhttp3.Request;

import okhttp3.Response;

public class EnqueueCallbackExample {

public static void main(String[] args) {

OkHttpClient client = new OkHttpClient();

String url = "https://example.com/file";

Callback callback = new Callback() {

@Override

public void onFailure(Request request, IOException e) {

System.out.println("请求失败: " + e.getMessage());

}

@Override

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

if (response.isSuccessful()) {

System.out.println("请求成功: " + response.body().string());

} else {

System.out.println("请求失败: " + response.code());

}

}

};

Request request = new Request.Builder()

.url(url)

.build();

client.newCall(request).enqueue(callback);

}

}

```

在这个示例中,我们使用了`enqueue`方法而不是`enqueueCallback`方法。这是因为我们现在使用的是OkHttp的较旧版本,而这个方法是较新版本中的推荐用法。如果你使用的是较新版本的OkHttp,你应该直接使用`enqueueCallback`方法。

这段代码是一个简单的HTTP请求示例,它使用了OkHttp库。下面是重构后的代码:

```java

import okhttp3.*;

public class HttpClientExample {

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {

Request request = new Request.Builder()

.url("http://publicobject.com/helloworld.txt")

.build();

client.newCall(request).enqueue(new Callback() {

@Override

public void onFailure(Call call, IOException e) {

e.printStackTrace();

}

@Override

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

try (ResponseBody responseBody = response.body()) {

if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

Headers responseHeaders = response.headers();

for (int i = 0, size = responseHeaders.size(); i < size; i++) {

System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));

}

System.out.println(responseBody.string());

}

}

});

}

}

```

重构后的代码将主要的几种请求方式的案例进行了简化,其他请求方式可以根据上述方法进行类似的实现。

你可以参考官网的请求案例讲解,其中有更多的案例详情,比如Post方式提交String, Post方式提交流,响应缓存等。

此外,OkHttpUtils工具类是一款基于 OkHttp 的封装库,由技术社区知名开发者 hongyangAndroid 创建并维护。它极大地简化了网络请求的过程,提供了简洁易用的API,允许开发者快速集成HTTP请求功能。

Slf4j

public class OkHttpUtils {

private static OkHttpClient okHttpClient = null;

public static OkHttpClient getOkHttpClient() {

if (okHttpClient != null) {

return okHttpClient;

}

synchronized (OkHttp3Demo.class) {

// 自定义连接池最大空闲连接数和等待时间大小,否则默认最大10个空闲连接

ConnectionPool connectionPool = new ConnectionPool(32, 10, TimeUnit.MINUTES);

okHttpClient = new OkHttpClient.Builder()

.connectTimeout(30, TimeUnit.SECONDS) // 连接超时

.writeTimeout(20,TimeUnit.SECONDS) // 写入超时

.readTimeout(20,TimeUnit.SECONDS) // 读取超时

.connectionPool(connectionPool) //

// .addInterceptor() 添加应用拦截器

// .addNetworkInterceptor() 添加网络拦截器

// .cache() 设置缓存

.build();

}

return client;

}

private static final OkHttpClient client = new OkHttpClient.Builder()

.connectTimeout(30, TimeUnit.SECONDS)

.build();

/**

* 同步GET请求

* @param url 请求地址

*/

public static String getRequest(String url) throws IOException {

Request request = new Builder().url(url).build();

Response response = execute(request);

if (response.isSuccessful()) {

return response.body().string();

} else {

throw new ArithmeticException("请求异常,错误码为: " + response.code());

}

}

/**

* 同步POST请求

* @param url 请求地址

* @param params 请求参数

*/

public static String postRequest(String url, Map params) throws IOException {

try {

MultipartBody body = new MultipartBody.Builder()

.setType(MediaType.parse("multipart/form-data"))

.addFormDataPart("username", params.get("username"))

.addFormDataPart("password", params.get("password"))

.build();

Request request = new Request.Builder()

.post(body)

.url(url)

.build();

Response response = execute(request);

if (response.isSuccessful()) {

return response.body().string();

} else {

throw new RuntimeException("请求异常,错误码为: " + response.code()); }

} catch (Exception e) { log.info("请求失败,错误信息为= {} ", e.getMessage()); } return null; } } /** * GET请求 */ public static Response execute(String url) throws Exception { Request request = new Builder().url(url).build(); Response response = client.newCall(request).execute(); return response; } /** * * 异步执行网络请求 */ /** * * @param request * @param callback * @throws Exception */ /** * 异步执行网络请求不带返回结果 */ /** * 异步执行网络请求不带返回结果 */ /** * 开启异步线程访问网络, 需要返回结果 */ /** * 开启异步线程访问网络,不需要返回结果(回调返回值为空) */

在撰写文献综述或者进行任何学术研究时,引用参考文献是非常重要的一部分。参考文献不仅可以证明你的研究基于可靠的来源,而且也可以提供进一步的阅读材料给读者。以下是一些关于如何正确地引用参考文献的建议:

1. 确保你理解了引用规则:不同的学科和出版物可能有不同的引用规则。例如,APA(美国心理学协会)和MLA(现代语言协会)是两种常见的引用风格。确保你了解并遵循你所在领域或出版物的具体规则。

2. 使用引文管理工具:引文管理工具如EndNote、Zotero和Mendeley可以帮助你管理和格式化你的引用。这些工具也可以帮助你在写作时自动插入正确的引用格式。

3. 在正文中明确引用来源:在你直接引用某项信息或观点的地方,你需要明确指出该信息的来源。这通常通过在句子中包含作者的名字、出版年份、文章标题和页码来完成。

4. 在参考文献列表中列出所有来源:在你的论文或报告的末尾,你需要提供一个完整的参考文献列表。这个列表应该按照你在正文中引用的顺序排列,每个来源都应该包括作者的名字、出版年份、文章标题、出版社和页码。

5. 仔细检查你的引用:最后,你需要仔细检查你的引用,确保它们都是准确的。错误的引用可能会导致抄袭的问题,所以在提交之前一定要进行校对。