Markdown版本筆記 | 我的GitHub首頁 | 我的博客 | 我的微信 | 我的郵箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
目錄
Recipes 食譜/知識點清單
Synchronous Get 同步Get
Asynchronous Get 異步Get
Accessing Headers 訪問頭信息
Posting a String 以Post發送字符串
Post Streaming 以Post發送流
Posting a File 以Post發送文件
Posting form parameters 以Post發送表單參數
Posting a multipart request 以Post發送多請求體的請求
Parse a JSON Response With Gson/Moshi 使用Gson來解析響應
Response Caching 緩存響應
Canceling a Call 取消一個請求
Timeouts 超時
Per-call Configuration 預配置
Handling authentication 處理身份驗證
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()方法,因為它將把整個文檔加載到內存中。在這種情況下,可以將響應體作為流來處理。
public void synchronousGet() throws Exception {
Request request = new Request.Builder()//Get請求
.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());//響應體
}
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以部分接收響應主體。
public void asynchronousGet() 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
通常,HTTP頭的工作方式類似於Map
:每個字段都有一個值或沒有值。 但是一些頭允許多個值,如Guava的Multimap。 例如,HTTP響應提供多個Vary(變化、不等)標頭是合法和通用的。 OkHttp的API嘗試使這兩種情況都舒適。
When writing request headers, use header(name, value) to set the only occurrence of name to value. If there are existing values, they will be removed before the new value is added. Use addHeader(name, value) to add a header without removing the headers already present.
當寫請求頭時,使用 header(name, value) 將設置唯一發生的(出現的)名稱的值。如果存在現有值,則在添加新值之前將刪除它們。 使用 addHeader(name, value) 來添加標題時,將不會移除已經存在的頭。
When reading response a header, use header(name) to return the last occurrence of the named value. Usually this is also the only occurrence! If no value is present, header(name) will return null. To read all of a field's values as a list, use headers(name).
當讀取響應一個頭時,使用 header(name) 返回最后一次出現的名稱的值。 通常這也是唯一發生的事情! 如果沒有值,header(name) 將返回null。 要讀取所有字段的值作為列表,請使用 headers(name) 。
To visit all headers, use the Headers class which supports access by index.
要訪問所有標題,請使用支持通過索引訪問的Headers類。
public void accessingHeaders() throws Exception {
Request request = new Request.Builder()
.url("https://api.github.com/repos/square/okhttp/issues")
.header("User-Agent", "OkHttp Headers.java")//使用 header 會移除已經存在的頭
.addHeader("Accept", "application/json; q=0.5")
.addHeader("Accept", "application/vnd.github.v3+json")//使用 addHeader 不會移除已經存在的頭
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
//訪問Header
System.out.println("【Server】" + response.header("Server"));
System.out.println("【Date】" + response.header("Date"));
System.out.println("【Vary】" + response.headers("Vary"));
}
Posting a String 以Post發送字符串
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文檔發送到一個將markdown作為HTML來顯示(呈現)的web服務器上。 因為整個請求體同時在內存中,所以請避免使用此API發送大型(大於1個MiB)文檔。
public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");
public void postString() 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))//以Post發送字符串,字符串是MARKDOWN文件中的內容
.build();
}
Post Streaming 以Post發送流
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緩沖池(sink:水槽、水池)。您編程時可能更喜歡使用OutputStream,您可以從BufferedSink.outputStream()中獲取。
public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");
public void postStreaming() throws Exception {
RequestBody requestBody = new RequestBody() {
@Override
public MediaType contentType() {
return MEDIA_TYPE_MARKDOWN;//內容類型為: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)//以Post發送流
.build();
}
Posting a File 以Post發送文件
It's easy to use a file as a request body.
使用OkHttp可以很容易將文件作為請求體來進行發送。
public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");
public void postFile() throws Exception {
File file = new File(Environment.getExternalStorageDirectory(), "README.md");
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))//以Post發送文件
.build();
}
Posting form parameters 以Post發送表單參數
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.Builder構建一個像HTML
<form>
標簽一樣工作的請求體。 將使用HTML兼容的表單URL編碼對名稱和值進行編碼。
public void postFormParameters() 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)//以Post發送表單參數
.build();
}
Posting a multipart request 以Post發送多請求體的請求
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.
MultipartBody.Builder可以構建與HTML文件上傳表單兼容的復雜請求體。多部分請求體的每個部分本身就是一個請求體,並且可以定義自己的頭。如果存在這樣的請求體部件,這些頭應該描述請求體部件主體,比如它的Content-Disposition(內容配置)。 如果這些請求體可用,那么Content-Length(內容長度)和Content-Type(內容類型)將自動添加到頭部字段。
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
public void postMultipartRequest() throws Exception {
RequestBody partRequestBody = RequestBody.create(MEDIA_TYPE_PNG, new File(Environment.getExternalStorageDirectory(), "logo.png"));
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("title", "Square Logo")
.addFormDataPart("image", "logo-square.png", partRequestBody)//多部分請求體的每個部分本身就是一個請求體
.build();
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + "...")
.url("https://api.imgur.com/3/image")
.post(requestBody)//以Post發送多請求體的請求
.build();
}
Parse a JSON Response With Gson/Moshi 使用Gson來解析響應
Moshi 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.
Moshi(以前推薦的是Gson,不知道什么時候改成了Moshi)是用於在JSON和Java對象之間進行轉換的一個方便的API。這里我們使用它來解碼來自GitHub API的JSON響應。
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。
private final Moshi moshi = new Moshi.Builder().build();
public void parseJSONResponseWithGson() 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 = new Gson().fromJson(response.body().charStream(), Gist.class);//使用Gson來解析JSON響應
Gist gist2 = moshi.adapter(Gist.class).fromJson(response.body().source());//使用Moshi來解析JSON響應
for (Map.Entry<String, Gist.GistFile> entry : gist.files.entrySet()) {
System.out.println("【Key】" + entry.getKey());
System.out.println("【Value】" + 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 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.
響應緩存使用HTTP頭進行所有配置。 您可以在請求頭部添加 Cache-Control:max-stale=3600,OkHttp的緩存將使其符合要求(存就會執行)。您的web服務器通過它自己的響應頭,來配置響應緩存的時間長短,如Cache-Control:max-age = 9600。有一些緩存頭強制緩存響應,強制網絡響應,或強制使用有條件的GET對網絡響應進行驗證。
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不滿足的請求響應。
public void responseCaching() throws Exception {
File cacheFile = new File(Environment.getExternalStorageDirectory().getAbsoluteFile(), "cache");//緩存路徑
Cache cache = new Cache(cacheFile, 10 * 1024 * 1024);//緩存大小10 MiB
client = new OkHttpClient.Builder()
.cache(cache)//緩存響應
.build();
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("【兩次響應結果是否相同】" + response1Body.equals(response2Body));
}
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.cancel()立即停止正在進行的通話。 如果線程當前正在寫請求或讀取響應,它將收到一個IOException。 當一個Call不再需要時,可以使用它來節省網絡(流量);例如,當您的用戶通過導航推出程序時。同步和異步呼叫都可以被取消。
public void cancelingCall() throws Exception {
Request request = new Request.Builder()
.url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay. 此網址延遲2秒后才處理請求。
.build();
final long startNanos = System.nanoTime();
final Call call = client.newCall(request);
// Schedule a job to cancel the call in 1 second. 在1秒內安排一個作業取消請求。
Executors.newScheduledThreadPool(1).schedule(() -> {
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.
當網絡請求不可到達時,Call請求會由於超時而導致失敗。網絡不可到達的原因可能是由於客戶端連接問題,服務器可用性問題,或客戶端和服務器之間的任何問題。 OkHttp支持連接超時、讀取超時以及寫入超時。
public void settTmeouts() throws Exception {
client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)//連接超時時間
.writeTimeout(10, TimeUnit.SECONDS)//寫入超時時間
.readTimeout(500, TimeUnit.MILLISECONDS)//讀取超時時間
.build();
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.body().string());
}
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, call OkHttpClient.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中,包括代理設置,超時和高速緩存。當您需要更改單個Call的配置時,請調用OkHttpClient.newBuilder()。這個方法返回一個Builder,該Builder與初始客戶端共享相同的連接池、分發器和配置。 在下面的示例中,我們將使用一個500毫秒的超時的請求,和另一個3000毫秒的超時的請求。
public void perCallConfiguration() throws Exception {
OkHttpClient copyClient = client.newBuilder()// Copy to customize OkHttp for this request.
.readTimeout(500, TimeUnit.MILLISECONDS)
.build();
Request request = new Request.Builder()
.url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay.
.build();
Response response = copyClient.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println("【響應結果】" + response.body().string());
}
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未授權 時,將要求身份驗證者提供憑證(證書)。 實現/用戶 應該構建一個新的請求,其中包含缺少的憑證(證書)。 如果沒有可用的憑據,返回null以跳過重試。
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) 對請求頭進行編碼。
//處理身份驗證
public void handlingAuthentication() throws Exception {
client = new OkHttpClient.Builder()
.authenticator((route, response) -> {//身份驗證
System.out.println("【Authenticating for response】" + response);
System.out.println("【Challenges】" + response.challenges());//獲取認證質疑的方案和領域
return response.request()
.newBuilder()
.header("Authorization", Credentials.basic("jesse", "password1"))//對請求頭進行編碼
.build();
})
.build();
Request request = new Request.Builder()
.url("http://publicobject.com/secrets/hellosecret.txt")
.build();
}
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來放棄(該請求)。例如,當這些確切的用戶憑證已經被嘗試時,用戶可能希望跳過重試:
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:
當你超過應用程序定義的嘗試連接限制(次數)時,你也可能希望跳過重試:
if (responseCount(response) >= 3) return null; // If we've failed 3 times, give up.
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;
}
2017-9-7
附件列表