OkHttp官方Wiki之Recipes【使用案例】

原文位置:https://github.com/square/okhttp/wiki/Recipes

We‘ve written some recipes that demonstrate证明、展示 how to solve common problems with OkHttp. Read through them to learn about how everything works together. Cut-and-paste these examples freely; that‘s what they‘re for.

本文将演示如何使用OkHttp来解决常见问题,了解每件事是如何一起工作的。

Synchronous Get,同步Get

Download a file, print its headers, and print its response body as a string.

下载一个文件,打印它的响应结果的响应头,并将它的响应体作为字符串打印出来。

The string() method on response body is convenient and efficient for small documents. But if the response body is large (greater than 1 MiB), avoid string() because it will load the entire document into memory. In that case, prefer to process the body as a stream.

响应体中的string()方法对于小文档来时是方便和高效的。但是如果响应体体积很大(大于1Mib),则应避免使用string()方法,因为它将把整个文档加载到内存中。在这种情况下,可以将响应体作为流来处理。

  1. //同步Get
  2. public void synchronousGet() throws Exception {
  3. Request request = new Request.Builder()
  4. .url("http://publicobject.com/helloworld.txt")
  5. .build();
  6. Response response = client.newCall(request).execute();
  7. if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  8. Headers responseHeaders = response.headers();
  9. for (int i = 0; i < responseHeaders.size(); i++) {
  10. System.out.println("【" + responseHeaders.name(i) + "】" + responseHeaders.value(i));
  11. }
  12. System.out.println("【响应结果】" + response.body().string());
  13. }

Asynchronous Get,异步Get

Download a file on a worker thread, and get called back when the response is readable. The callback is made after the response headers are ready. Reading the response body may still block. OkHttp doesn‘t currently offer asynchronous APIs to receive a response body in parts.

在工作线程上下载一个文件,当响应结果可读时就会得到回调调用。回调是在响应头准备好之后进行的。读取响应体可能仍然会阻塞。在一些地方,OkHttp目前还没有提供异步api来接受响应主体。

  1. //异步Get
  2. public void asynchronousGet() throws Exception {
  3. Request request = new Request.Builder()
  4. .url("http://publicobject.com/helloworld.txt")
  5. .build();
  6. client.newCall(request).enqueue(new Callback() {
  7. @Override
  8. public void onFailure(Call call, IOException e) {
  9. e.printStackTrace();
  10. }
  11. @Override
  12. public void onResponse(Call call, Response response) throws IOException {
  13. if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  14. Headers responseHeaders = response.headers();
  15. for (int i = 0, size = responseHeaders.size(); i < size; i++) {
  16. System.out.println("【" + responseHeaders.name(i) + "】" + responseHeaders.value(i));
  17. }
  18. System.out.println("【响应结果】" + response.body().string());
  19. }
  20. });
  21. }

Accessing Headers,访问Header

典型的HTTP头部使用Map

  1. //访问Header
  2. public void accessingHeaders() throws Exception {
  3. Request request = new Request.Builder()
  4. .url("https://api.github.com/repos/square/okhttp/issues")
  5. .header("User-Agent", "OkHttp Headers.java")
  6. .addHeader("Accept", "application/json; q=0.5")
  7. .addHeader("Accept", "application/vnd.github.v3+json")
  8. .build();
  9. Response response = client.newCall(request).execute();
  10. if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  11. System.out.println("【Server】" + response.header("Server"));
  12. System.out.println("【Date】" + response.header("Date"));
  13. System.out.println("【Vary】" + response.headers("Vary"));
  14. }

Posting a String,Post发送字符串

本例子使用HTTP POST将请求体发送到服务器,将一个markdown文档发送到一个将markdown作为HTML的web服务器上。因为整个请求体在内存中,所以使用这个API避免发送(大于1个Mib)文档。

  1. //Post发送字符串
  2. public void postString() throws Exception {
  3. String postBody = ""
  4. + "Releases\n"
  5. + "--------\n"
  6. + "\n"
  7. + " * _1.0_ May 6, 2013\n"
  8. + " * _1.1_ June 15, 2013\n"
  9. + " * _1.2_ August 11, 2013\n";
  10. Request request = new Request.Builder()
  11. .url("https://api.github.com/markdown/raw")
  12. .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
  13. .build();
  14. Response response = client.newCall(request).execute();
  15. if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  16. System.out.println("【响应结果】" + response.body().string());
  17. }

Post Streaming,Post发送流

在本例中将一个请求体作为流来进行发送。这个请求体的内容在被写入时生成。这个示例的请求体数据直接流到Okio缓冲池中,在程序中将作为OutputStream,可以从BufferedSink.outputStream()方法中获得。

  1. //Post发送流
  2. public void postStreaming() throws Exception {
  3. RequestBody requestBody = new RequestBody() {
  4. @Override
  5. public MediaType contentType() {
  6. return MEDIA_TYPE_MARKDOWN;
  7. }
  8. @Override
  9. public void writeTo(BufferedSink sink) throws IOException {
  10. sink.writeUtf8("Numbers\n");
  11. sink.writeUtf8("-------\n");
  12. for (int i = 2; i <= 997; i++) {
  13. sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
  14. }
  15. }
  16. private String factor(int n) {
  17. for (int i = 2; i < n; i++) {
  18. int x = n / i;
  19. if (x * i == n) return factor(x) + " × " + i;
  20. }
  21. return Integer.toString(n);
  22. }
  23. };
  24. Request request = new Request.Builder()
  25. .url("https://api.github.com/markdown/raw")
  26. .post(requestBody)
  27. .build();
  28. Response response = client.newCall(request).execute();
  29. if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  30. System.out.println("【响应结果】" + response.body().string());
  31. }

Posting a File,Post发送文件

使用OkHttp可以很容易将文件作为请求体来进行发送。

  1. //Post发送文件
  2. public void postFile() throws Exception {
  3. File file = new File(Environment.getExternalStorageDirectory(), "README.md");
  4. Request request = new Request.Builder()
  5. .url("https://api.github.com/markdown/raw")
  6. .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
  7. .build();
  8. Response response = client.newCall(request).execute();
  9. if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  10. System.out.println("【响应结果】" + response.body().string());
  11. }

Posting form parameters,发布形式参数

使用FormBody.Builder表单建造者来构建一个像HTML<form>标签一样工作的请求体。键值对将使用与HTML兼容的表单URL编码进行编码。

  1. //发布形式参数
  2. public void postFormParameters() throws Exception {
  3. RequestBody formBody = new FormBody.Builder()
  4. .add("search", "Jurassic Park")
  5. .build();
  6. Request request = new Request.Builder()
  7. .url("https://en.wikipedia.org/w/index.php")
  8. .post(formBody)
  9. .build();
  10. Response response = client.newCall(request).execute();
  11. if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  12. System.out.println("【响应结果】" + response.body().string());
  13. }

Posting a multipart request,发布多请求体的请求

MultipartBody.Builder可以构建与HTML文件上传表单兼容的复杂请求体。多部件请求体中的每一个部件本身就是一个请求体,并且可以定义它自己的头部。如果存在这样的请求体部件,这些头部应该描述请求体部件主体,比如它的内容配置Content-Disposition。同时如果这些请求体可用,那么内容长度Content-Length和内容类型Content-Type将自动添加到头部字段。

  1. //发布多请求体的请求
  2. public void postMultipartRequest() throws Exception {
  3. RequestBody requestBody = new MultipartBody.Builder()
  4. .setType(MultipartBody.FORM)
  5. .addFormDataPart("title", "Square Logo")
  6. .addFormDataPart("image", "logo-square.png",
  7. RequestBody.create(MEDIA_TYPE_PNG, new File(Environment.getExternalStorageDirectory(), "logo.png"))).build();
  8. Request request = new Request.Builder()
  9. .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
  10. .url("https://api.imgur.com/3/image")
  11. .post(requestBody)
  12. .build();
  13. Response response = client.newCall(request).execute();
  14. if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  15. System.out.println("【响应结果】" + response.body().string());
  16. }

Parse a JSON Response With Gson,使用Gson来解析JSON响应

Gson是一个用于在JSON和Java对象之间进行转换的一个方便的API。在本例中将使用Gson来解码来自GitHub Api的JSON响应。 
注意,下面代码中的ResponseBody.charStream()方法使用Content-Type响应头部来选择在解码响应体时应使用哪个字符集。如果没有指定字符集,则默认为UTF-8。

  1. //使用Gson来解析JSON响应
  2. public void parseJSONResponseWithGson() throws Exception {
  3. Request request = new Request.Builder()
  4. .url("https://api.github.com/gists/c2a7c39532239ff261be")
  5. .build();
  6. Response response = client.newCall(request).execute();
  7. if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  8. Gist gist = new Gson().fromJson(response.body().charStream(), Gist.class);
  9. for (Map.Entry<String, Gist.GistFile> entry : gist.files.entrySet()) {
  10. System.out.println("【Key】" + entry.getKey());
  11. System.out.println("【Value】" + entry.getValue().content);
  12. }
  13. }
  14. static class Gist {
  15. Map<String, GistFile> files;
  16. static class GistFile {
  17. String content;
  18. }
  19. }

Response Caching,响应缓存

要使OkHttp来缓存响应,用户需要创建一个可以读写的缓存目录,以及设置缓存大小的限制。该缓存目录应该是私有的private,不受信任的应用程序不能读取它的内容。 
让多个缓存同时访问同一个缓存目录是错误的。大多数应用程序都应该只调用一次new OkHttpClient( ),并为其配置缓存,并在任何地方使用该OkHttp实例。否则,两个缓存实例将相互影响,破坏响应缓存,并可能破坏您的程序。 
响应缓存使用HTTP头部来进行所有的配置。用户可以在请求头部添加Cache-Control:max-stale=3600的字段,OkHttp的缓存就会执行。用户的web服务器配置响应缓存的响应头部来设置响应缓存的大小,例如Cache-Control:max-age=9600。有一些缓存头部可以强制缓存响应,强制网络响应,或强制使用有条件的GET对网络响应进行验证。

当不使用缓存时,可以使用CacheControl.FORCE_NETWORK来防止使用缓存的响应。当只使用缓存,而不使用网络获取数据时,可以使用CacheControl.FORCE_CACHE。注意:当使用FORCE_CACHE时,响应结果需要从网络获取,OkHttp将返回504 Unsatisfiable Reques 的响应结果。

  1. //响应缓存
  2. public void responseCaching() throws Exception {
  3. Cache cache = new Cache(new File(Environment.getExternalStorageDirectory().getAbsoluteFile(), "cache"),
  4. 10 * 1024 * 1024);// 10 MiB
  5. client = new OkHttpClient.Builder()
  6. .cache(cache)
  7. .build();
  8. Request request = new Request.Builder()
  9. .url("http://publicobject.com/helloworld.txt")
  10. .build();
  11. Response response1 = client.newCall(request).execute();
  12. if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);
  13. String response1Body = response1.body().string();
  14. System.out.println("【Response 1 response】" + response1);
  15. System.out.println("【Response 1 cache response】" + response1.cacheResponse());
  16. System.out.println("【Response 1 network response】" + response1.networkResponse());
  17. Response response2 = client.newCall(request).execute();
  18. if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);
  19. String response2Body = response2.body().string();
  20. System.out.println("【Response 2 response】" + response2);
  21. System.out.println("【Response 2 cache response】" + response2.cacheResponse());
  22. System.out.println("【Response 2 network response】" + response2.networkResponse());
  23. System.out.println("【两次响应结果是否相同】" + response1Body.equals(response2Body));
  24. }

Canceling a Call,取消一个请求

当发起网络请求后,可以使用Call.cancel()来停止正在进行的调用。如果一个线程正在写请求或者读取响应,将收到一个IOException异常。当一个Call不再需要使用时,可以使用cancel()来保护网络,例如当用户退出应用程序时。注意,同步和异步的调用都可以被取消。

  1. //取消一个请求
  2. public void cancelingCall() throws Exception {
  3. Request request = new Request.Builder()
  4. .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
  5. .build();
  6. final long startNanos = System.nanoTime();
  7. final Call call = client.newCall(request);
  8. // Schedule a job to cancel the call in 1 second.
  9. Executors.newScheduledThreadPool(1).schedule(() -> {
  10. System.out.printf("【】%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f);
  11. call.cancel();
  12. System.out.printf("【】%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f);
  13. }, 1, TimeUnit.SECONDS);
  14. try {
  15. System.out.printf("【】%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f);
  16. Response response = call.execute();
  17. System.out.printf("【】%.2f Call was expected to fail, but completed: %s%n", (System.nanoTime() - startNanos) / 1e9f, response);
  18. } catch (IOException e) {
  19. System.out.printf("【】%.2f Call failed as expected: %s%n", (System.nanoTime() - startNanos) / 1e9f, e);
  20. }
  21. }

Timeouts,响应超时

当网络请求不可到达时,call请求会由于超时而导致失败。网络不可到达的原因可能是由于客户端连接问题,服务器可用性问题或者其他原因造成的。OkHttp支持连接超时,读取超时以及写入超时。

  1. //响应超时
  2. public void settTmeouts() throws Exception {
  3. client = new OkHttpClient.Builder()
  4. .connectTimeout(10, TimeUnit.SECONDS)
  5. .writeTimeout(10, TimeUnit.SECONDS)
  6. .readTimeout(500, TimeUnit.MILLISECONDS)
  7. .build();
  8. Request request = new Request.Builder()
  9. .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
  10. .build();
  11. Response response = client.newCall(request).execute();
  12. System.out.println("【响应结果】" + response.body().string());
  13. }

Per-call Configuration,配置Call

所有的HTTP客户端配置都存在于OkHttpClient中,包括代理设置,超时和缓存。当需要更改单个Call调用的配置时,请调用OkHttpClient.newBuilder()方法,这个方法同样返回一个OkHttp构建器,该构建器与初始客户端OkHttpClient.Builder()共享相同的连接池Connection Pool,分发器Dispatcher和配置。在下面的例子中,将使用一个500毫秒的超时的请求和另一个3000毫秒的超时的请求。

  1. //配置Call
  2. public void perCallConfiguration() throws Exception {
  3. OkHttpClient copyClient = client.newBuilder()// Copy to customize OkHttp for this request.
  4. .readTimeout(500, TimeUnit.MILLISECONDS)
  5. .build();
  6. Request request = new Request.Builder()
  7. .url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay.
  8. .build();
  9. Response response = copyClient.newCall(request).execute();
  10. if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  11. System.out.println("【响应结果】" + response.body().string());
  12. }

Handling authentication,处理身份验证

OkHttp可以自动重试未经身份验证的请求,例如当响应状态吗是401未经授权时,会要求身份验证者提供证书。用户应该构建一个新的请求,并附带缺少的验证证书。如果没有可用的证书,则返回null以跳过重试。 
使用Response.challenges()方法来获取任何身份验证的方案和领域。当需要完成一个基本的身份验证时,可以使用Credentials.basic(username,password)方法来对请求头进行编码。

  1. //处理身份验证
  2. public void handlingAuthentication() throws Exception {
  3. client = new OkHttpClient.Builder()
  4. .authenticator((route, response) -> {
  5. System.out.println("【Authenticating for response】" + response);
  6. System.out.println("【Challenges】" + response.challenges());
  7. return response.request()
  8. .newBuilder()
  9. .header("Authorization", Credentials.basic("jesse", "password1"))
  10. .build();
  11. })
  12. .build();
  13. Request request = new Request.Builder()
  14. .url("http://publicobject.com/secrets/hellosecret.txt")
  15. .build();
  16. Response response = client.newCall(request).execute();
  17. if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  18. System.out.println("【响应结果】" + response.body().string());
  19. }

为了避免在身份验证无效时进行多次重试,可以返回null来放弃该请求。例如,当这些确切的用户凭证已经被尝试时,用户可能希望跳过重试:

  1. if (credential.equals(response.request().header("Authorization"))) {
  2. return null; // If we already failed with these credentials, don‘t retry.
  3. }

当你超过应用程序定义的尝试连接限制时,你可能希望跳过重试:

  1. if (responseCount(response) >= 3) {
  2. return null; // If we‘ve failed 3 times, give up.
  3. }

上文中的responseCount()方法的源码如下所示:

  1. private int responseCount(Response response) {
  2. int result = 1;
  3. while ((response = response.priorResponse()) != null) {
  4. result++;
  5. }
  6. return result;
  7. }

2017-6-19

附件列表

时间: 2024-10-18 09:18:04

OkHttp官方Wiki之Recipes【使用案例】的相关文章

OkHttp 官方中文文档

OkHttp官方中文文档 本文结构 Calls Connections Recipes Interceptors HTTPS 本文翻译来自 官方OkHttp Wiki OkHttp官方中文文档 一Calls 1 请求 2 响应 3重写请求 4重写响应 5后续请求 6请求重试 7 呼叫 8调度 二Connections 1URLs URLs摘要 2 Addresses 3 Routes 4Connections 三Recipes 1同步获取 2异步获取 3访问头 4Posting a String

Gentoo Linux安装详解--根据官方WiKi整理

1. 前期准备 远程登录: 开启ssh服务: /etc/init.d/sshd start 设置密码: passwd 以便使用putty.ssh client远程登录上传stage等(有时在线下载很慢,而局域网上传很快) 准备磁盘: 分区: fdisk /dev/sda /dev/sda1 : /boot 100M(32-100M) 设启动笔记-a/dev/sda2 : / 20G/dev/sda3 : /home 20G/dev/sda5 : /swap 1G (内存< 512 MB,分区分配

.Net轻量级ORM-NPoco的使用方法-摘自NPoco国外官方Wiki

文章引用自NPoco官方Wiki,地址:https://github.com/schotime/NPoco/wiki,因公司网络不稳定,有时无法访问,特将其摘抄. Home Welcome to the NPoco wiki! NPoco is a fork of PetaPoco with a handful of extra features. Getting Started: Your first query 1 public class User 2 { 3 public int Use

iosOpenDev-install 失败官方wiki无法解决看这里(尝试有效)

https://github.com/kokoabim/iOSOpenDev/wiki/Troubleshoot http://blog.csdn.net/bluesky_03/article/details/41911613 从theos到iosopendev.iosopendev可以使用xcode来完成插件开发. --------------begin 多余的theos{安装iosopendev,不需要安装theos,下面的做法不是必需的:[  // 获取theos,在这里并不是必须的exp

Okhttp使用详解

在Android开发中,发送HTTP请求是很常见的.SDK中自带的HttpURLConnection虽然能基本满足需求,但是在使用上有诸多不便,为此,square公司实现了一个HTTP客户端的类库--Okhttp . Okhttp是一个支持HTTP 和 HTTP/2 的客户端,可以在Android和Java应用程序中使用,其具有以下特点: 1. API设计轻巧,基本上通过几行代码的链式调用即可获取结果. 2. 既支持同步请求,也支持异步请求.同步请求会阻塞当前线程,异步请求不会阻塞当前线程,异步

程序员的量化交易之路(33)--QuantConnect之案例1

转载需注明:http://blog.csdn.net/minimicall ,http://cloudtrade.top/ 分析Cointrader有一定层度了,发现它毕竟不是一个产品,没有得到验证.在架构.编码等方面都非常的不规范. 想编写一个云交易平台,任道而重远.我们需要参照一些成熟的架构. Quantopian的zipline不行,因为我就是看到它不行,所以才去分析Cointrader的. 现在这两个,一个压根就不是云平台,一个是不成熟,所以我只能去分析剩下的一个QuantConnect

Andriod OKHttp源码解析

前言:对于 OkHttp 勤快学QKXue.NET接触的时间其实不太长,一直都是使用Retrofit + OkHttp 来做网络请求的,但是有同学说面试的时候可能会问框架源码,这样光是会用是不够的,于是便萌生了通一通OkHttp源码的念头.经过大约一周的时间,源码看了个大概(说来惭愧,也就知道里面的原理),这里变向大家介绍一下我的所得,希望对大家能有所帮助.这里推荐两篇博文: OkHttp 官方教程解析 - 彻底入门 OkHttp 使用 和 拆轮子系列:拆 OkHttp 前者能够让你入门OkHt

Android OkHttp使用与分析

安卓开发领域,很多重要的问题都有了很好的开源解决方案,例如网络请求 OkHttp + Retrofit 简直就是不二之选."我们不重复造轮子不表示我们不需要知道轮子该怎么造及如何更好的造!",在用了这些好轮子将近两年之后,现在是时候拆开轮子一探究竟了.本文基于 OkHttp 截至 2016.7.11 的最新源码对其进行了详细分析. 1,整体思路 从使用方法出发,首先是怎么使用,其次是我们使用的功能在内部是如何实现的,实现方案上有什么技巧,有什么范式.全文基本上是对 OkHttp 源码的

Android网络请求框架—OKHttp 源码解析

总体流程 整个流程是,通过OkHttpClient将构建的Request转换为Call,然后在RealCall中进行异步或同步任务,最后通过一些的拦截器interceptor发出网络请求和得到返回的response. 将流程大概是这么个流程,大家可以有个大概的印象,继续向下看: OkHttp流程图.jpg 为了让大家有更深的印象,我准备追踪一个GET网络请求的具体流程,来介绍在源码中发生了什么. GET请求过程 这是利用OkHttp写一个Get请求步骤,这里是一个同步的请求,异步的下面也会说: