OkHttp整理
来源:互联网 发布:淘宝男鞋店推荐 编辑:程序博客网 时间:2024/06/15 15:32
导入
compile 'com.squareup.okhttp3:okhttp:3.8.1'
混淆:
-dontwarn okio.**-dontwarn javax.annotation.Nullable-dontwarn javax.annotation.ParametersAreNonnullByDefault
最新版本请看github
使用示例:
同步Get
response.body().string()
对于小文件来说很方便,但是如果文件大小大于1MB的话,尽量避免使用response.body().string()
,因为这会把这个文件都加载到内存当中,应该使用流来读取比较好。
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()); }
异步Get
在一个子线程进行下载,有response的时候才触发回调。回调在response的headers可用的时候触发,但是读取response的body还是可能会阻塞住。OkHttp当前不提供异步API来分部分接收response的body。
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 { 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(response.body().string()); } }); }
headers的获取
Http的headers一般是类似Map<String, String>
的结构,即每个header字段对应着一个或者零个值。不过也存在一个header对应多个值的情况。
编写请求headers的时候,如果是一对一形式,则调用header(name, value)
来设置,也就是旧值会覆盖新值;如果是一对多形式,则调用addHeader(name, value)
。
读取响应的headers的时候,调用header(name)
来获取该键名对应的最后一个值,如果没有就返回null。如果要返回该键名对应的所有值,则调用headers(name)
。
如果要获取所有的headers,可以使用Headers
这个类来按序号获取header。
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")); }
Post发送字符串
以下示例把一个markdown文件发送到服务器,该服务器会把markdown渲染为HTML。注意此时请求的body是在内存中的,所以尽量避免发送大于1MB的文件。
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请求
以下的示例以写入Okio缓存池的形式发送Post请求,如果想要使用OutputStream可以调用BufferedSink.outputStream()
来获取。
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()); }
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(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); System.out.println(response.body().string()); }
Post发送表单
使用FormBody.Builder()
来构建一个表单形式的请求体。表单的键值对会使用适配HTML表单的URL编码处理。
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(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); System.out.println(response.body().string()); }
Post一个multipart请求
MultipartBody.Builder
可以构建一个与HTML文件上传表单匹配的复杂的请求体。multipart请求体的本身也是一个请求体,还可以定义自己的headers。如果有相应headers的话,应该是用来描述这个小请求体的,比如说Content-Disposition
。如果文件长度或者文件类型可知的话,就会自动加上Content-Length
和Content-Type
头部。
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 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(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); System.out.println(response.body().string()); }
使用Gson解析JSON格式的response
response.body().charStream()
是使用Content-Type
这个响应头指定的字符集来解码响应体。如果没有指定的话默认使用UTF-8。
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的缓存
如果要缓存response的话,要有一个限制大小的可读写的缓存目录。注意这个目录必须是private,其他应用不能访问到。
大部分应用都应该只调用一次new OkHttpClient()
,配置好相应缓存实例,保证只有一个缓存实例读写缓存目录避免出错。
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.
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.Builder() .cache(cache) .build(); } 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)); }
使用CacheControl.FORCE_NETWORK
和CacheControl.FORCE_CACHE
来强制使用网络或者强制使用缓存来获取response。注意当使用强制缓存的时候,如果需要网络请求才能获取到response,就会返回一个504 Unsatisfiable Request
的response。
取消请求
调用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); 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); } }
设置超时
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(); Response response = client.newCall(request).execute(); System.out.println("Response completed: " + response); }
单次请求设置
所有的Http设置都存在于OkHttpClient当中。如果需要改变设置的时候,可以调用OkHttpClient.newBuilder()
来返回一个与原client相同连接池、分配者和配置的builder,然后可以进行修改,比如下面对于两个请求配置了两个不同的读取超时时间:
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(); Response response = client.newCall(request).execute(); System.out.println("Response completed: " + response); }
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); } }
处理认证:
对于未认证的请求,OkHttp会自动重试。如果请求返回401 Not Authorized
,就需要提供一个Authenticator进行认证,如果不提供就不会重试了。提供Authenticator的话就是创建一个包含Authenticator的新request。
调用Response.challenges()
来获取认证要求的schemes和范围。调用Credentials.basic(username, password)
来设置用户名密码。
private final OkHttpClient client; public Authenticate() { client = new OkHttpClient.Builder() .authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { 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(); } }) .build(); } public void run() throws Exception { 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()); }
假如已经重试过了可以返回null取消重试:
if (credential.equals(response.request().header("Authorization"))) { return null; }
或者根据重试次数决定是否重试:
... { 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; }
- OkHttp整理
- retrofit与okhttp知识点整理
- OkHttp
- okhttp
- OkHttp
- OkHttp
- OkHttp
- okhttp
- OKHTTP
- okhttp
- Okhttp
- okHttp
- OkHttp
- okhttp
- Okhttp
- okhttp
- OkHttp
- okhttp
- STL之map
- CentOs 7.3 —— 使用rescue找回被删除的系统文件
- input文本框没有onchange事件,可以通过下面两种方法实现当input内容改变时触发事件
- 每日一题(41)—— 数组和链表的区别
- [BZOJ]1071: [SCOI2007]组队 STL优先队列
- OkHttp整理
- Linux 内存查看
- git的操作流程
- Android 定义uid权限
- P1197 [JSOI2008]星球大战
- JavaScript两种数据类型对比
- ssh框架登录注册
- HDFS Commands
- Apache http2.4.27解压版环境配置问题总结(win10)