前言
okhttp已经出來很久咯,从最开始的httpClient和HttpURLConnection,再到后來的各种封装,Volley是吧!其实如果不用开源框架,自己进行封装的话,不同的人封装的也不同。所以呀这個okhttp就出來啦!如果你要知道volley相关的文章,可以点击下面这些链接哦!
Volley/Xutils/UIL(Universal-Image-Loader)对大图片处理算法源码分析
Volley框架使用
Android-volley-NetWorkImageView的使用
okhttp wiki
原文地址: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(),因为这样干会把整个文件加载到内存中去,你果文件很大,那么最好是把响应体以流的形式输出。
//创建OkHttpClient对象
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());
}
synchronous 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去接收响应体。
//创建这個OkHttpClient对象
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());
}
});
}
Accessing Headers(头部信息)
Typically HTTP headers work like a Map<String, String>: each field has one value or none. But some headers permit multiple values, like Guava’s Multimap. For example, it’s legal and common for an HTTP response to supply multiple Vary headers. OkHttp’s APIs attempt to make both cases comfortable.
典型的这個HTTP头部信息呢,就像这样子Map<String,String>(键值对的形式),第一個键有一個或者沒有对应的值。但是捏,这個有些头部信息允许多個值,就像这個Guava’sMultimap一样。例如,Http响应的头部信息多個变量是合法的。OkHttp的API呢尽量地使两种情况协调。
When writing request headers, use header(name, value) to set the only occurrence of name tovalue. If there are existing values, they will be removed before the new value is added. UseaddHeader(name, value) to add a header without removing the headers already present.
当我们写请求头信息的时候,用这個header(name,value)方法去设置仅仅这個键和值对应,如果已经存在了值 ,那么原來的值就会被删除掉,新的值被添加进去。使用addHeader(name,value)这個方法去添加,则不会删除已经存在的值。
To visit all headers, use the Headers class which supports access by index.
如果你想遍历所有的值,那么你可以使用Headers这個类去通过索引去获取到对应的值。
//老规矩,创建OkHttpClient对象
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") //这里是请求的Url
.header("User-Agent", "OkHttp Headers.java") //这里是请求的头部信息
.addHeader("Accept", "application/json; q=0.5") //这里为的就是演示使用这個方法添加,会不会覆盖掉,具体看上面的文字描述
.addHeader("Accept", "application/vnd.github.v3+json") //the same
.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(上传字符串)
Use an HTTP POST to send a request body to a service. This example posts a markdown document to a web service that renders markdown as HTML. Because the entire request body is in memory simultaneously, avoid posting large (greater than 1 MiB) documents using this API.
使用这個Http的Post请求去发送一個请求体到服务呼呼大睡 ,这個例子呢,就是把一個markdown文件发送到web服务器作为了個html的形式來读取。因为这请求体的实体会在这個内存里面,所以呢,避免使用这個API去提交大文件(大于1MiB的文件)
//创建一個常量,这玩意就是用于說明格式和编码的
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
//创建一個OkHttpClient对象
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(提交数据流)
Here we POST a request body as a stream. The content of this request body is being generated as it’s being written. This example streams directly into the Okio buffered sink. Your programs may prefer an OutputStream, which you can get from BufferedSink.outputStream().
这里的话,我们就以流的形式作为这個请求体提交数据。请求体的内容生成和写的是一样的。下面这個例子呢直接进和这個Okio buffered sink.在你的项目中更常見的是从这個BufferedSink.outputStream()方法里获取到这個输出流对象。
//创建这個类型常量
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
//创建这個OkHttpClient对象
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 a File(提交文件)
It’s easy to use a file as a request body.
把这文件当作这個请求体非常简单哈!
//创建格式常量
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
//创建OkHttpClient对象
private final OkHttpClient client = new OkHttpClient();
//创建子线程
public void run() throws Exception {
//创建file对象
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(以参数的形式提交数据)
Use FormBody.Builder to build a request body that works like an HTML <form> tag. Names and values will be encoded using an HTML-compatible form URL encoding.
使用这個FormBody去构建这個请求体,就像这個Html的表格标签样。这键和值都会用这個URL编码进行转码。
//创建OkHttpClient实例对象
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());
}
Posting a multipart request(提交多個请求)
MultipartBody.Builder can build sophisticated request bodies compatible with HTML file upload forms. Each part of a multipart request body is itself a request body, and can define its own headers. If present, these headers should describe the part body, such as its Content-Disposition. The Content-Length and Content-Type headers are added automatically if they’re available.
使用MultparBody.Builder 可以构建符合HTML文件上复杂的请求表单。每一個请求体就是它自己的请求体,还可以定义好自己的头部信息,如果已经存在,那些头部信息应该描述请求体,例如它的内容位置(Content-Disposition),内容长度,内容类型,这些都会自动添加。前提是它们是可用的。
//定义客户端ID
private static final String IMGUR_CLIENT_ID = "...";
//提交的数据类型
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
//创建OkHttpClient对象
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());
}
//定义客户端ID
private static final String IMGUR_CLIENT_ID = "...";
//提交的数据类型
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
//创建OkHttpClient对象
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());
}
Parse a JSON Response With Gson(用Gson解析JSON数据)
Gson is a handy API for converting between JSON and Java objects. Here we’re using it to decode a JSON response from a GitHub API.
Gson呢,是现成的API,用于转换这個JSON和Java对象。这里呢,就用Gson來对这個从Github返回的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來选择编码形式。要是沒有的话,默认就是UTF-8啦。
//创建OkHttpClient对象
private final OkHttpClient client = new OkHttpClient();
//窗机Gson对象,这個要导包哦,或添加依赖。
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(缓存)
To cache responses, you’ll need a cache directory that you can read and write to, and a limit on the cache’s size. The cache directory should be private, and untrusted applications should not be able to read its contents!
为了缓存这個返回的数据,你需要一個可读可写的缓存,还有这個缓存的最大尺寸。这段缓存应该是私有的,其他不符合的应用不可以读到里面的内容。
It is an error to have multiple caches accessing the same cache directory simultaneously. Most applications should call new OkHttpClient() exactly once, configure it with their cache, and use that same instance everywhere. Otherwise the two cache instances will stomp on each other, corrupt the response cache, and possibly crash your program.
多块缓存同时联上同一块缓存区域是不对的,多数的应用应该调用new OkHttpClient()方法一次(相当于单例)。配置相关的缓存,那里使用都用它一個即可。否则,多块缓存就会有冲突,可能会让你的应用挂掉。
Response caching uses HTTP headers for all configuration. You can add request headers likeCache-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.
响应缓存会使用HTTP头部信息來配置相关信息,你可以添加类似于Cache-content:max-stale=3600这们的请求头部信息,OkHttp的缓存会识别它,你的服务器可以像Cache-control:max-age=9600这样子來配置缓存的时候,缓存和头部信息会在网络请求相应时强制去返回,后者强制网络以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));
}
PS:在图书馆哈,看到上面的代码和后面的长度,暂时不写注释了,只翻译英文,反正你是看得懂的嘛!
To prevent a response from using the cache, use CacheControl.FORCE_NETWORK. To prevent it from using the network, use CacheControl.FORCE_CACHE. Be warned: if you use FORCE_CACHE and the response requires the network, OkHttp will return a 504 Unsatisfiable Request response.
防止响应是从缓存中返回的,可以使用CacheControl.FORCE_NETWORK來解决。避免从这個网络中获取,可以使用CacheControl.FORCE_CACHE來控制。注意:如果你使用FORCE_CACHE,那么如果这個去请求网络,OkHttp会给你返回504请求失败的响应。
Canceling a Call(取消请求)
Use Call.cancel() to stop an ongoing call immediately. If a thread is currently writing a request or reading a response, it will receive an IOException. Use this to conserve the network when a call is no longer necessary; for example when your user navigates away from an application. Both synchronous and asynchronous calls can be canceled.
使用Call.cancle()可以马上停止一個正在进行的请求。如果一個线程正在写或者读取响应数据,那么它会接收到一個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);
}
}
Timeouts(超时)
Use timeouts to fail a call when its peer is unreachable. Network partitions can be due to client connectivity problems, server availability problems, or anything between. OkHttp supports connect, read, and write timeouts.
当访问不到的时候,就会超时。网络不能访问的原因可能是因为客户端的链接问题,服务器的问题,或者这两者之间。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);
}
Per-call Configuration(每一请求的配置)
All the HTTP client configuration lives in OkHttpClient including proxy settings, timeouts, and caches. When you need to change the configuration of a single call, callOkHttpClient.newBuilder(). This returns a builder that shares the same connection pool, dispatcher, and configuration with the original client. In the example below, we make one request with a 500 ms timeout and another with a 3000 ms timeout.
所有的http请求客户端,都在这個OkHttpClient的基础上,包括代理的设置,超时,还有挂掉。当你需要改变这些配置信息为单一请求时,你可以调用OkHttpClient.newBuilder()这個方法。这样子就会返回了個builder,这個builder共享同样的链接池,路由和配置这些源客户端。在下面这個例子,我们一個是配置500毫秒超时,另外一個则是3000毫秒超时。
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 can automatically retry unauthenticated requests. When a response is 401 Not Authorized, an Authenticator is asked to supply credentials. Implementations should build a new request that includes the missing credentials. If no credentials are available, return null to skip the retry.
OkHttp可以自动地重新发起沒有认证的请求。当一個请求是401沒认证时,认证者要证书,项目就应该创建新的请求,并且要包括赠书,如果沒有可用的证书,就会返回空來重定向。
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.
使用Response.challenges()这個方法來获取约束和任何证书认证。当通過基本认证之后,使用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());
}
To avoid making many retries when authentication isn’t working, you can return null to give up. For example, you may want to skip the retry when these exact credentials have already been attempted:
防止证书失效时的重试,你可以返回null來放弃,例如:当那些证书都 試過了,你可能想不干了,那么你就可以返回null
if (credential.equals(response.request().header("Authorization"))) {
return null; // If we already failed with these credentials, don't retry.
}
This above code relies on this responseCount() method:
上面这些代码都依赖responseCount方法:
private int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}