OkHttp应该是目前最完善,也是相当流行的一个底层网络请求库。Google都在用,所以有必要深入了解一下,刚好最近在重构公司项目的网络层,就顺便梳理一下。
———–12.29————
最近暂时没有时间详细整理了。就简单过了一下官方文档。
以下取自官方文档。

网络请求

同步Get方法

以下样例代码下载一个文件,打印headers,打印字符串形式的 response body

1234567891011121314151617
private final OkHttpClient client = new OkHttpClient();

 public void run() throws Exception { Request request = new Request.Builder() .url("http://publicobject.com/helloworld.txt") .build();

 Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

 Headers responseHeaders = response.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 大于1M时,避免用string(),因为它会加载整个文件到内存,此时应该把body用stream的形式来处理.

异步Get方法

在工作线程下载一个文件,并且当response准备好了的时候回调Callback。 response headers 准备好了的时候,就走回调。读response body 也会阻塞线程,OkHttp暂时没有提供额外的异步
APIs来获得response body。

123456789101112131415161718192021222324
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(Request request, IOException throwable) { throwable.printStackTrace(); }

 @Override public void onResponse(Response response) throws IOException { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

 Headers responseHeaders = response.headers(); for (int i = 0; i < responseHeaders.size(); i++) { System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i)); }

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

Accessing Headers

典型的HTTP请求头,像Map<String, String>那样工作,每个field有一个值或者没有,但是有的headers允许有多个值,就像Guava’s Multimap.比如,一个HTTP response 提供多个多样的headers是合法且普遍的。 OkHttp’s APIs 兼容这两种情况。
注意:当写request headers时,header(name, value) 设置唯一的键值对,这会覆盖已有的值。而用方法 addHeader(name, value) 来添加header时,不会移除已有的header。
相应的header(name)来获取最后的这个name的相应value,通常这也是唯一的,如果没有就返回null 。headers(name)用来read所有的field的值,已list的形式。
想要看所有的headers,使用支持通过索引访问的类Headers

1234567891011121314151617
private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception { 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();

 Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

 System.out.println("Server: " + response.header("Server")); System.out.println("Date: " + response.header("Date")); System.out.println("Vary: " + response.headers("Vary"));}

Posting a String

由于这个request body同时要完全加载到内存,所以避免用个API来posting大于1M的文件。

123456789101112131415161718192021222324
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();

 Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

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

Post Streaming

一下示例代码使用到了Okio的buffered sink,你可能更喜欢使用OutputStream,你可以通过BufferedSink.outputStream()来获得。

1234567891011121314151617181920212223242526272829303132333435363738
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();

 Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

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

Posting文件

简单

123456789101112131415161718
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();

 Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

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

Posting form parameters(格式化参数)

用FormEncodingBuilder来构建一个request body,像HTML的

标签一样工作,键值对将以兼容HTML的URL编码方式被组织。

12345678910111213141516
private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception { RequestBody formBody = new FormEncodingBuilder() .add("search", "Jurassic Park") .build(); Request request = new Request.Builder() .url("https://en.wikipedia.org/w/index.php") .post(formBody) .build();

 Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

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

Posting一个multipart request(复合请求)

MultipartBuilder可以构建复杂的request bodies,兼容html文件上传forms。复合请求的每个request body分别可以定义自己的headers,如果存在,这些headers分别描述相应的body,例如Content-Disposition。The Content-Length and Content-Type headers 会自动添加。

12345678910111213141516171819202122232425262728
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/image RequestBody requestBody = new MultipartBuilder() .type(MultipartBuilder.FORM) .addPart( Headers.of("Content-Disposition", "form-data; name=\"title\""), RequestBody.create(null, "Square Logo")) .addPart( Headers.of("Content-Disposition", "form-data; name=\"image\""), 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();

 Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

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

用Gson解析JSON返回结果

Gson是一个便利的API用来转换JSON和java对象。
Note that ResponseBody.charStream() uses the Content-Type response header to select which charset to use when decoding the response body. It defaults to UTF-8 if no charset is specified.
注意ResponseBody.charStream()根据返回头的Content-Type来选择相应的编码方式,来解码response body。如果没有定义charset,默认是用UTF-8。

123456789101112131415161718192021222324
private final OkHttpClient client = new OkHttpClient();private final Gson gson = new Gson();

public void run() throws Exception { Request request = new Request.Builder() .url("https://api.github.com/gists/c2a7c39532239ff261be") .build(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

 Gist gist = gson.fromJson(response.body().charStream(), Gist.class); for (Map.Entry<String, GistFile> entry : gist.files.entrySet()) { System.out.println(entry.getKey()); System.out.println(entry.getValue().content); }}

static class Gist { Map<String, GistFile> files;}

static class GistFile { String content;}

Response Caching

缓存请求结果,你需要一个你可以读写的限制大小的缓存目录,缓存目录应该是私有的,禁止未信任的程序随便读取。
Response caching uses HTTP headers for all configuration. You can add request headers like Cache-Control: max-stale=3600 and OkHttp’s cache will honor them. Your webserver configures how long responses are cached with its own response headers, like Cache-Control: max-age=9600. There are cache headers to force a cached response, force a network response, or force the network response to be validated with a conditional GET.
Response caching完全用HTTP headers来配置,你可以添加像这样的请求头:Cache-Control: max-stale=3600,你的web服务器可以这样的请求头Cache-Control: max-age=9600来配置请求结果缓存时间。可以设置相应的headers来强制缓存response,force a network response, or force the network response to be validated(经过验证的) with a conditional GET.

123456789101112131415161718192021222324252627282930313233
private final OkHttpClient client;

public CacheResponse(File cacheDirectory) throws Exception { int cacheSize = 10 * 1024 * 1024; // 10 MiB Cache cache = new Cache(cacheDirectory, cacheSize);

 client = new OkHttpClient(); client.setCache(cache);}

public void run() throws Exception { Request request = new Request.Builder() .url("http://publicobject.com/helloworld.txt") .build();

 Response response1 = client.newCall(request).execute(); if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);

 String response1Body = response1.body().string(); System.out.println("Response 1 response: " + response1); System.out.println("Response 1 cache response: " + response1.cacheResponse()); System.out.println("Response 1 network response: " + response1.networkResponse());

 Response response2 = client.newCall(request).execute(); if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);

 String response2Body = response2.body().string(); System.out.println("Response 2 response: " + response2); System.out.println("Response 2 cache response: " + response2.cacheResponse()); System.out.println("Response 2 network response: " + response2.networkResponse());

 System.out.println("Response 2 equals Response 1? " + response1Body.equals(response2Body));}

阻止使用缓存的response,使用CacheControl.FORCE_NETWORK。阻止使用网络,使用 CacheControl.FORCE_CACHE。注意,如果你使用FORCE_CACHE而response需要网络支持的话,OkHttp 将返回一个 504 Unsatisfiable Request response.

取消一个call

用Call.cancel()会立刻停止一个进行中的请求,如果一个线程正在writing一个request,或者在reading一个response,将会抛IOException。
如果你的用户操作离开了app,所有的同步异步请求都应该取消。
你可以用tags来同时取消多个请求,用RequestBuilder.tag(tag)在创建请求的时候指定一个tag,用OkHttpClient.cancel(tag)取消所有有这个tag的请求。

123456789101112131415161718192021222324252627282930
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);

 try { System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f); 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); }}

Timeouts

1234567891011121314151617
  private final OkHttpClient client;

public ConfigureTimeouts() throws Exception { client = new OkHttpClient(); client.setConnectTimeout(10, TimeUnit.SECONDS); client.setWriteTimeout(10, TimeUnit.SECONDS); client.setReadTimeout(30, TimeUnit.SECONDS);}

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();

 Response response = client.newCall(request).execute(); System.out.println("Response completed: " + response);}

Per-call Configuration 某个请求特殊配置

所有的HTTP client配置都在OkHttpClient,包括代理,超时,缓存,你想为单个请求改变配置的话,clone the OkHttpClient,这个会返回一个浅(shallow)copy让你来单独定制,如下,你make了一个500ms超时的和3000ms超时的请求。

123456789101112131415161718192021222324252627
private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception { Request request = new Request.Builder() .url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay. .build();

 try { OkHttpClient cloned = client.clone(); // Clone to make a customized OkHttp for this request. cloned.setReadTimeout(500, TimeUnit.MILLISECONDS);

 Response response = cloned.newCall(request).execute(); System.out.println("Response 1 succeeded: " + response); } catch (IOException e) { System.out.println("Response 1 failed: " + e); }

 try { OkHttpClient cloned = client.clone(); // Clone to make a customized OkHttp for this request. cloned.setReadTimeout(3000, TimeUnit.MILLISECONDS);

 Response response = cloned.newCall(request).execute(); System.out.println("Response 2 succeeded: " + response); } catch (IOException e) { System.out.println("Response 2 failed: " + e); }}

新版3.0修改为如下:

12345678910111213141516171819202122232425262728293031
private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception { Request request = new Request.Builder() .url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay. .build();

 try { // Copy to customize OkHttp for this request. OkHttpClient copy = client.newBuilder() .readTimeout(500, TimeUnit.MILLISECONDS) .build();

 Response response = copy.newCall(request).execute(); System.out.println("Response 1 succeeded: " + response); } catch (IOException e) { System.out.println("Response 1 failed: " + e); }

 try { // Copy to customize OkHttp for this request. OkHttpClient copy = client.newBuilder() .readTimeout(3000, TimeUnit.MILLISECONDS) .build();

 Response response = copy.newCall(request).execute(); System.out.println("Response 2 succeeded: " + response); } catch (IOException e) { System.out.println("Response 2 failed: " + e); }}

Handling authentication(身份验证)

OkHttp可以自动重操作没有验证通过的请求,当一个response是401 Not Authorized(未授权,未认可),表示认证者需要你提供相应证书,实现是应该新建一个带有相应缺失证书的请求,如果获取不到证书,返回null跳过retry。
Use Response.challenges() to get the schemes and realms of any authentication challenges.
When fulfilling a Basic challenge, use Credentials.basic(username, password) to encode the request header.

123456789101112131415161718192021222324252627
private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception { client.setAuthenticator(new Authenticator() { @Override public Request authenticate(Proxy proxy, Response response) { System.out.println("Authenticating for response: " + response); System.out.println("Challenges: " + response.challenges()); String credential = Credentials.basic("jesse", "password1"); return response.request().newBuilder() .header("Authorization", credential) .build(); }

 @Override public Request authenticateProxy(Proxy proxy, Response response) { return null; // Null indicates no attempt to authenticate. } });

 Request request = new Request.Builder() .url("http://publicobject.com/secrets/hellosecret.txt") .build();

 Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

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

身份验证失效的时候就别多次retry了,return null放弃就好。

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

You may also skip the retry when you’ve hit an application-defined attempt limit:

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

 private int responseCount(Response response) { int result = 1; while ((response = response.priorResponse()) != null) { result++; } return result; }

转载于:https://www.cnblogs.com/android-blogs/p/5778916.html

Android OkHttp经验小结相关推荐

  1. Android技能树 — 网络小结(6)之 OkHttp超超超超超超超详细解析

    前言: 本文也做了一次标题党,哈哈,其实写的还是很水,各位原谅我O(∩_∩)O. 介于自己的网络方面知识烂的一塌糊涂,所以准备写相关网络的文章,但是考虑全部写在一篇太长了,所以分开写,希望大家能仔细看 ...

  2. Android okHttp网络请求之缓存控制Cache-Control

    前言: 前面的学习基本上已经可以完成开发需求了,但是在项目中有时会遇到对请求做个缓存,当没网络的时候优先加载本地缓存,基于这个需求我们来学习一直okHttp的Cache-Control. okHttp ...

  3. Android工作经验三年总结。(零基础自学Android)

    开篇 两年前写过一篇 Android工作经验一年总结 之后还是不少的小伙伴加到了我的QQ分享经验,现在基本上都找到了比较满意的工作踏上了脱发之路.大概有20几个吧,都是经历和我差不多,也是愿意是吃苦和 ...

  4. am335x usb linux配置,AM335xUSB调试的经验小结.PDF

    AM335x USB 调试的经验小结 Yaoming 硬件调试 • 首先,将USB设备放到PC等设备上以确认该设备本身是否正常. • 可以根据AM335x 的Schematics Check list ...

  5. Android OkHttp(1)

     Android OkHttp(1) OkHttp是一个流行的第三方开源网络请求框架,在目前的一些APP开发中比较流行.Android平台开源的网络请求框架不少,比如常见的Volley, Asyn ...

  6. JavaScript学习笔记(十)——学习心得与经验小结

    JavaScript学习笔记(十)--学习心得与经验小结 目前我先列好提纲,利用每晚和周末的闲暇时间,将逐步写完 ^_^ 转载于:https://www.cnblogs.com/mixer/archi ...

  7. Android okHttp上传图片

    今天,简单讲讲Android 使用OK HTTP上传图片. 不废话了,直接上代码: /*** 上传图片* @param url* @param imagePath 图片路径* @return 新图片的 ...

  8. android okhttp使用cookie请求

    今天,简单讲讲Android的http使用cookie请求. 现在,做一个功能,出现一个奇怪现象,就是老是登陆成功后,发送请求获取消息,老是提示我没有登陆.我自己都不知道是什么原因,这个也是我对网络通 ...

  9. android OKHttp的基本使用详解

    今天,简单讲讲Android里如何使用OKHttp. Android框架系列: 一.android EventBus的简单使用 二.android Glide简单使用 三.android OKHttp ...

最新文章

  1. JGG:青岛大学苏晓泉团队利用条件致病菌指数评估环境微生物风险
  2. Hibernate一对多(注解)
  3. spring下redis开发环境搭建
  4. 初识ABP vNext(11):聚合根、仓储、领域服务、应用服务、Blob储存
  5. 2016校招真题编程练习——微信红包(腾讯)
  6. 计算机视觉专业要学什么课程,[08本]“计算机视觉基础”课程介绍和课件
  7. C# 字段、属性、成员变量
  8. 微信小程序微商城(二):电商首页轮播、分类导航和新品特卖实现
  9. 12v服务器电源改可调_IBM服务器电源,艾默生7001578-j000 改造DIY成可调电源详细教程...
  10. JS动态添加元素后事件不起作用失效
  11. 计算机屏幕显示电缆借口,电脑打不开,显示电缆线没有连接是什么意思?
  12. 阿里云kafka安装
  13. 揭秘公共WiFi安全黑幕
  14. 5.14.1.14 LBA Status Information
  15. java-操作 Excel
  16. nacative mysql_分析996个词根在各大考纲词汇中的作用(二)
  17. 【时序】基于 TCN 的用于序列建模的通用卷积和循环网络的经验评估
  18. matlab中sym与syms的对比
  19. linux中mysql的安装与卸载_linux的mysql安装与卸载
  20. 2022考研王道计算机408pdf(王道计算机组成原理+王道操作系统+王道计算机网络+王道数据结构)

热门文章

  1. Boosting Xgboost
  2. Flask cookie
  3. MySQL Bit Functions and Operators(二进制方法)
  4. MySQL Miscellaneous Functions(ip uuid方法)
  5. linux rename
  6. Pandas GroupBy对象 索引与迭代
  7. 使用plsql创建oracle,在Oracle下使用PLSQL Developer创建简单的触发器
  8. vant部署_记录mpvue+vant-weapp的使用(一):安装配置使用,引入vant-weapp
  9. VMware vSAN 6.7实战(六)配置vSAN存储策略
  10. Java基础学习总结(172)——手写Java 重试机制