1. 简介

okhttp是我们在Android开发中十分常用的一个网络请求框架。

2. 常用API总结

下面将介绍okhttp3中的一些常用的api(okhttp3完整文档如下,可自行参阅:https://square.github.io/okhttp/3.x/okhttp/)。

2.1 OkHttpClient

OkHttpClient可以理解为是一个构造Call对象的“工厂”。OkHttpClient实例创建方式有两种:

(1)直接通过new的方式实例化:

// The singleton HTTP client.
public final OkHttpClient client = new OkHttpClient();

(2)通过OkHttpClient.Builder构造实例,此种方式支持自定义部分行为:

// The singleton HTTP client.
public final OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new HttpLoggingInterceptor()).cache(new Cache(cacheDir, cacheSize)).build();

我们在使用OkHttpClient实例时,最好确保它在整个应用中都是唯一的单例。client实例会持有它自己的连接池和线程池,确保client的全局唯一可以让我们在所有的请求中复用这些连接和线程,这有助于减少内存资源的消耗。

为此,官方还提供了newBuilder()的api来帮助我们创建支持自定义参数的可全局共享的OkHttpClient实例:

   OkHttpClient eagerClient = client.newBuilder().readTimeout(500, TimeUnit.MILLISECONDS).build();Response response = eagerClient.newCall(request).execute();

之后,官方文档中还提到,OkHttp中被挂起的线程和连接都将会在保持空闲时自动回收。如果我们想要主动释放资源,可以使用如下方式,之后所有的Call对象执行请求都将被拒绝。

 client.dispatcher().executorService().shutdown();

清理连接池可以使用如下方式(连接池的守护线程可能不会立即退出):

 client.connectionPool().evictAll();

如果希望关闭缓存,可以使用如下方式:

client.cache().close();

2.2 Request

Request对象是http请求的抽象(包括请求地址,请求方法,请求头,请求体等内容)。通常情况下,我们可以通过Request.Builder来创建该对象:

    Request request = new Request.Builder().url("https://api.github.com/repos/square/okhttp/issues").header("User-Agent", "OkHttp Headers.java").addHeader("Accept", "application/json; q=0.5").build();

我们通过request对象的url,method,headers,body等方法能够获取到请求的对应信息。

2.3 Response

Response对象是http响应的抽象(包括响应头,响应体等内容)。

2.4 ResponseBody

ResponseBody抽象的是响应体(包括服务端响应的原始字节信息),我们可以通过response.body()来获取该对象。

需要注意的是,ResponseBody对象必须在我们使用完后关闭。因为每一个ResponseBody都是由有限的资源提供支持,例如套接字(实时网络响应)和打开的文件(用于缓存响应体)。如果没有关闭ResponseBody,将会导致资源的泄露,严重时可能会使得应用变得缓慢或者是崩溃。ResponseBody和Response均实现了closeable接口,我们可以通过如下方法来关闭:

Response.close();
Response.body().close();
Response.body().source().close();
Response.body().charStream().close();
Response.body().byteStream().close();

对于同步调用,我们可以使用try代码块,编译器会帮我们在隐含的finally代码块中调用close方法。例如:

Call call = client.newCall(request);
try (Response response = call.execute()) {... // Use the response.
}

异步调用中也是类似的:

Call call = client.newCall(request);
call.enqueue(new Callback() {public void onResponse(Call call, Response response) throws IOException {try (ResponseBody responseBody = response.body()) {... // Use the response.}}public void onFailure(Call call, IOException e) {... // Handle the failure.}
});

2.5 Call

Call对象抽象的是一个正在准备执行的请求。它代表着一次完整的请求和响应,可以被取消,但是不能被执行两次。

3. 使用案例

以下例子均来源于官网。

3.1 同步Get请求

通过Get请求去服务器上获取helloworld.txt的文本内容:

private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {Request request = new Request.Builder().url("https://publicobject.com/helloworld.txt").build();// client.newCall(request)会返回一个Call对象,通过调用Call对象的execute方法就会执行同步请求,并返回一个Response对象try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) {throw new IOException("Unexpected code: " + response);}// 获取响应头的集合Headers responseHeaders = reponse.headers();// 遍历并打印响应头的内容for (int i = 0; i < responseHeaders.size(); i++) {System.out.println(responseHeaders.name(i) + " : " + responseHeaders.value(i));}// 打印响应体System.out.println(response.body().string());}
}

在上面的代码中,我们通过response.body().string()获取到了响应体的完整内容。但是如果在响应体的内容(字节数)比较大的情况下,就不建议采用这种方法来获取,因为response.body().string()会默认将整个响应体内容加载到内存中,此时我们最好使用字节流的形式来读取。

3.2 异步Get请求

在3.1中,我们学会了如何通过okhttp来发送同步请求,不过大家应该都知道,Android中的UI线程是不允许发送同步请求的,因为这会阻塞主线程,从而导致ANR。下面将介绍如何在okhttp中通过异步的方式来发送请求:

private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt").build();// 通过调用Call对象的enqueue方法就会执行异步请求,并且会根据请求成功与否执行相应的回调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 {// 非主线程,如果要执行UI操作,需要使用runOnUiThreadtry (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());}}});
}

3.3 请求头和响应头

下面的例子展示了如何设置请求头以及获取响应头:

private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {// 请求头的设置// header()会覆盖之前已设置的同名请求头,而addHeader()不会Request request = new Request.Builder().url("https://api.github.com/repos/square/okhttp/issues").header("User-Agent", "OkHttp Headers.java").addHeader("Accept", "application/json; q=0.5").addHeader("Accept", "application/vnd.github.v3+json").build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);// response.header(headerName)获取的是响应头对应字段的最新值System.out.println("Server: " + response.header("Server"));System.out.println("Date: " + response.header("Date"));// 获取响应头集合System.out.println("Vary: " + response.headers("Vary"));}}

3.4 Post请求(提交字符串)

下面的例子中通过Post请求提交了一串字符串到服务端:

public static final MediaType MEDIA_TYPE_MARKDOWN= MediaType.parse("text/x-markdown; charset=utf-8");private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {String postBody = ""+ "Releases\n"+ "--------\n"+ "\n"+ " * _1.0_ May 6, 2013\n"+ " * _1.1_ June 15, 2013\n"+ " * _1.2_ August 11, 2013\n";Request request = new Request.Builder().url("https://api.github.com/markdown/raw").post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody)).build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println(response.body().string());}}

需要注意的是,整个请求体对象Request都是在内存中的,所以要避免提交的字符串内容过多(不要超过1MB)。

3.5 Post请求(以字节流的形式提交请求体)

public static final MediaType MEDIA_TYPE_MARKDOWN= MediaType.parse("text/x-markdown; charset=utf-8");private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {RequestBody requestBody = new RequestBody() {@Override public MediaType contentType() {return MEDIA_TYPE_MARKDOWN;}@Override public void writeTo(BufferedSink sink) throws IOException {sink.writeUtf8("Numbers\n");sink.writeUtf8("-------\n");for (int i = 2; i <= 997; i++) {sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));}}private String factor(int n) {for (int i = 2; i < n; i++) {int x = n / i;if (x * i == n) return factor(x) + " × " + i;}return Integer.toString(n);}};Request request = new Request.Builder().url("https://api.github.com/markdown/raw").post(requestBody).build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println(response.body().string());}}

3.6 Post请求(提交文件)

public static final MediaType MEDIA_TYPE_MARKDOWN= MediaType.parse("text/x-markdown; charset=utf-8");private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {File file = new File("README.md");Request request = new Request.Builder().url("https://api.github.com/markdown/raw").post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file)).build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println(response.body().string());}}

3.7 Post请求(以表单形式提交参数)

通过FormBody.Builder我们可以构建表单参数形式的请求体(类似于HTML中的<form>标签),键值对将以兼容HTML表单的方式进行编码。

  private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {RequestBody formBody = new FormBody.Builder().add("search", "Jurassic Park").build();Request request = new Request.Builder().url("https://en.wikipedia.org/w/index.php").post(formBody).build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println(response.body().string());}}

此种方式Post的请求头中Content-Type字段的值是:application/x-www-form-urlencoded。

3.8 Post请求(以MultiPart的格式提交文件,分块请求)

 /*** The imgur client ID for OkHttp recipes. If you're using imgur for anything other than running* these examples, please request your own client ID! https://api.imgur.com/oauth2*/private static final String IMGUR_CLIENT_ID = "...";private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/imageRequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("title", "Square Logo").addFormDataPart("image", "logo-square.png",RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png"))).build();Request request = new Request.Builder().header("Authorization", "Client-ID " + IMGUR_CLIENT_ID).url("https://api.imgur.com/3/image").post(requestBody).build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println(response.body().string());}}

此种方式Post的请求头中Content-Type字段的值是:multipart/form-data,常用于以表单形式上传文件。详细区别可以参见这篇博客:深入解析 multipart/form-data。

3.9 取消一个请求

通过调用Call对象的cancel方法可以立刻停止一个进行中的请求,如果某个线程当前正在执行请求或读取响应,它将会收到一个IOException的异常。

无论是同步请求还是异步请求都能够被取消,在恰当的时候取消请求可以让我们节省网络资源。

  private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {Request request = new Request.Builder().url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay..build();final long startNanos = System.nanoTime();final Call call = client.newCall(request);// Schedule a job to cancel the call in 1 second.executor.schedule(new Runnable() {@Override public void run() {System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f);call.cancel();System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f);}}, 1, TimeUnit.SECONDS);System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f);try (Response response = call.execute()) {System.out.printf("%.2f Call was expected to fail, but completed: %s%n",(System.nanoTime() - startNanos) / 1e9f, response);} catch (IOException e) {System.out.printf("%.2f Call failed as expected: %s%n",(System.nanoTime() - startNanos) / 1e9f, e);}}

3.10 超时设置

okhttp请求超时设置方法如下:

  private final OkHttpClient client;public ConfigureTimeouts() throws Exception {client = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).build();}public void run() throws Exception {Request request = new Request.Builder().url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay..build();try (Response response = client.newCall(request).execute()) {System.out.println("Response completed: " + response);}}

4. 参考文章

官方文档:https://square.github.io/okhttp/

OkHttp使用完全教程:https://www.jianshu.com/p/ca8a982a116b

Okhttp3基本使用:https://www.jianshu.com/p/da4a806e599b

okhttp3使用总结相关推荐

  1. Static interface methods are only supported starting with Android N (--min-api 24): void okhttp3.log

    错误内容如下 Error: Static interface methods are only supported starting with Android N (--min-api 24): vo ...

  2. 在okhttp3,WebView中忽略HTTPS证书校验

    在APP开发过程中,后台使用的可能是自签的Https证书,如果不忽略证书校验,会出现Trust anchor for certification path not found的错误 Okhttp3忽略 ...

  3. android get请求最长字符,Android OKHTTP3的GET和POST方法(带basic auth)

    使用前需要在Gradle Script中的build gradle中引入: compile 'com.squareup.okio:okio:1.13.0' compile 'com.squareup. ...

  4. Java封装OkHttp3工具类

    点击关注公众号,Java干货及时送达  作者:如漩涡 https://blog.csdn.net/m0_37701381 Java封装OkHttp3工具类,适用于Java后端开发者 说实在话,用过挺多 ...

  5. android端使用http2.0,android Retrofit2+okHttp3使用总结

    使用前准备 Build.gradle文件配置 dependencies配置 compile 'com.squareup.retrofit2:retrofit:2.0.0' compile 'com.s ...

  6. OKHTTP3源码和设计模式(下篇)

    ​ 在<OKHTTP3源码和设计模式(上篇)>,中整体介绍了 OKHttp3 的源码架构,重点讲解了请求任务的分发管理和线程池以及请求执行过程中的拦截器.这一章我们接着往下走认识一下 OK ...

  7. OkHttp3源码详解(三) 拦截器-RetryAndFollowUpInterceptor

    最大恢复追逐次数: private static final int MAX_FOLLOW_UPS = 20; 处理的业务: 实例化StreamAllocation,初始化一个Socket连接对象,获 ...

  8. java 使用http2.0_【Java】okhttp3如何发送http2请求?

    问题背景: 最近想用springboot2.0搭建支持http2.0的服务器.搭好了之后,通过Chrome访问,显示http协议版本为h2(服务器升级成功,证书配置成功). 现在想自己写个客户端进行测 ...

  9. OkHttp3 websocket

    使用OkHttp3之Websocket实现长连接 这个发图片也没成功,发字符是可以的. 首先在在build.gradle中添加对Okhttp的支持 compile 'com.squareup.okht ...

  10. 使用OkHttp3之Websocket实现长连接

    compile 'com.squareup.okhttp3:okhttp:3.8.1' compile 'com.squareup.okhttp3:mockwebserver:3.8.1' 布局文件 ...

最新文章

  1. es6常用语法和特性
  2. 苹果工具条_苹果发布iOS 13.4首个测试版:能让iPhone变身为车钥匙
  3. mysql Navcat触发器生成订单号
  4. [Linux] 进程间通信
  5. Spring学习笔记(三)
  6. 此异常最初是在此调用堆栈中引发的:_【8】进大厂必须掌握的面试题Java面试异常和线程...
  7. 【LeetCode笔记】17.电话号码的字母组合(Java、DFS)
  8. oracle 执行sql,Oracle动态执行SQL
  9. 技术要点|Python监控学生端电脑屏幕自动识别学习状态
  10. java自定义按钮代码_用于短代码的WP Tiny MCE帖子编辑器上的自定义按钮
  11. 06-列空间和零空间
  12. Hive之行转列/列转行
  13. [渝粤教育] 西南科技大学 土木工程材料 在线考试复习资料
  14. php数据库随机选择,php – 在MySQL数据库中选择两个随机行
  15. 学习《鸟哥的Linux私房菜》后的感想
  16. 【python】六一新玩法turtle画哆啦A梦
  17. 微信小程序 自定义组件之《转盘》
  18. python:等间距分割pdf文件
  19. web前端移动端课程之canvas教程系列
  20. **京东撸货是什么,京东撸货具体怎么玩,能不能赚钱,我来告诉你**

热门文章

  1. 我拒绝公司疫情期间降薪的四个理由,99%老板听了无言以对
  2. 每天定时发送邮件提醒
  3. hi3559av100/hi3519av100/hi3516dv300/hi3516cv500
  4. “小身材高精度”千巡翼X1多场景应用案例
  5. 刘长乐:服务好用户是每个凤凰人的责任
  6. 我的微语录周记2011-11-14---2011-…
  7. 【面试宝典】--- 第一篇
  8. python opencv 实现基于深度学习的超分辨率处理
  9. AVPlayer 基础用法
  10. 机器学习不神秘!手把手教你用R语言打造文本分类器