Android OkHttp教程解析

 最近半年來身邊開發的朋友愈來愈多的提到OkHttp,上谷歌百度一下,確實OkHttp成了時下最火的HTTP框架,因而我也開始放下Volley,轉而關注OkHttp,五一期間仔細看了官方WiKi介紹(我喜歡學習官方的文檔),如今把本身整理的官方教程分享給你們,但願給初學者帶來幫助。
       OkHttp官網地址:http://square.github.io/okhttp/
       OkHttp GitHub地址:https://github.com/square/okhttp
官網的自我介紹:
       HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP efficiently makes your stuff load faster and saves bandwidth.
OkHttp is an HTTP client that’s efficient by default:php

•   HTTP/2 support allows all requests to the same host to share a socket. • Connection pooling reduces request latency (if HTTP/2 isn’t available). • Transparent GZIP shrinks download sizes. • Response caching avoids the network completely for repeat requests.

       OkHttp perseveres when the network is troublesome: it will silently recover from common connection problems. If your service has multiple IP addresses OkHttp will attempt alternate addresses if the first connect fails. This is necessary for IPv4+IPv6 and for services hosted in redundant data centers. OkHttp initiates new connections with modern TLS features (SNI, ALPN), and falls back to TLS 1.0 if the handshake fails.
       Using OkHttp is easy. Its request/response API is designed with fluent builders and immutability. It supports both synchronous blocking calls and async calls with callbacks.
       OkHttp supports Android 2.3 and above. For Java, the minimum requirement is 1.7.
       歸納起來講OkHttp是一款優秀的HTTP框架,它支持get請求和post請求,支持基於Http的文件上傳和下載,支持加載圖片,支持下載文件透明的GZIP壓縮,支持響應緩存避免重複的網絡請求,支持使用鏈接池來下降響應延遲問題。html

配置方法

(一)導入Jar包
點擊下面連接下載最新v3.2.0 JAR
http://repo1.maven.org/maven2/com/squareup/okhttp3/okhttp/3.2.0/okhttp-3.2.0.jar
(二)經過構建方式導入
MAVENjava

<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
  <version>3.2.0</version> </dependency>

GRADLEandroid

compile 'com.squareup.okhttp3:okhttp:3.2.0'

基本要求

Requests(請求)

       每個HTTP請求中都應該包含一個URL,一個GET或POST方法以及Header或其餘參數,固然還能夠含特定內容類型的數據流。git

Responses(響應)

       響應則包含一個回覆代碼(200表明成功,404表明未找到),Header和定製可選的body。github

基本使用

       在平常開發中最經常使用到的網絡請求就是GET和POST兩種請求方式。web

HTTP GET

OkHttpClient client = new OkHttpClient(); String run(String url) throws IOException { Request request = new Request.Builder().url(url).build(); Response response = client.newCall(request).execute(); if (response.isSuccessful()) { return response.body().string(); } else { throw new IOException("Unexpected code " + response); } }

Request是OkHttp中訪問的請求,Builder是輔助類,Response即OkHttp中的響應。
Response類:json

public boolean isSuccessful() Returns true if the code is in [200..300), which means the request was successfully received, understood, and accepted. response.body()返回ResponseBody類

能夠方便的獲取stringapi

public final String string() throws IOException Returns the response as a string decoded with the charset of the Content-Type header. If that header is either absent or lacks a charset, this will attempt to decode the response body as UTF-8. Throws: IOException

固然也能獲取到流的形式:
public final InputStream byteStream()緩存

HTTP POST

POST提交Json數據

public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); OkHttpClient client = new OkHttpClient(); String post(String url, String json) throws IOException { RequestBody body = RequestBody.create(JSON, json); Request request = new Request.Builder() .url(url) .post(body) .build(); Response response = client.newCall(request).execute(); f (response.isSuccessful()) { return response.body().string(); } else { throw new IOException("Unexpected code " + response); } }

使用Request的post方法來提交請求體RequestBody
POST提交鍵值對
OkHttp也能夠經過POST方式把鍵值對數據傳送到服務器

OkHttpClient client = new OkHttpClient(); String post(String url, String json) throws IOException { RequestBody formBody = new FormEncodingBuilder() .add("platform", "android") .add("name", "bug") .add("subject", "XXXXXXXXXXXXXXX") .build(); Request request = new Request.Builder() .url(url) .post(body) .build(); Response response = client.newCall(request).execute(); if (response.isSuccessful()) { return response.body().string(); } else { throw new IOException("Unexpected code " + response); } }

案例

佈局文件:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:orientation="horizontal"> <Button android:id="@+id/bt_get" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="烏雲Get請求"/> <Button android:id="@+id/bt_post" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="烏雲Post請求"/> </LinearLayout> <TextView android:id="@+id/tv_show" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>

這裏寫圖片描述
Java代碼:
因爲android自己是不容許在UI線程作網絡請求操做的,因此咱們本身寫個線程完成網絡操做

import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import com.squareup.okhttp.FormEncodingBuilder; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; import com.squareup.okhttp.RequestBody; import com.squareup.okhttp.Response; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button bt_get; private Button bt_post; final OkHttpClient client = new OkHttpClient(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); bt_get=(Button)findViewById(R.id.bt_get); bt_post=(Button)findViewById(R.id.bt_post); bt_get.setOnClickListener(this); bt_post.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()){ case R.id.bt_get: getRequest(); break; case R.id.bt_post: postRequest(); break; } } private void getRequest() { final Request request=new Request.Builder() .get() .tag(this) .url("http://www.wooyun.org") .build(); new Thread(new Runnable() { @Override public void run() { Response response = null; try { response = client.newCall(request).execute(); if (response.isSuccessful()) { Log.i("WY","打印GET響應的數據:" + response.body().string()); } else { throw new IOException("Unexpected code " + response); } } catch (IOException e) { e.printStackTrace(); } } }).start(); } private void postRequest() { RequestBody formBody = new FormEncodingBuilder() .add("","") .build(); final Request request = new Request.Builder() .url("http://www.wooyun.org") .post(formBody) .build(); new Thread(new Runnable() { @Override public void run() { Response response = null; try { response = client.newCall(request).execute(); if (response.isSuccessful()) { Log.i("WY","打印POST響應的數據:" + response.body().string()); } else { throw new IOException("Unexpected code " + response); } } catch (IOException e) { e.printStackTrace(); } } }).start(); } }

執行結果:
這裏寫圖片描述

官方Recipes

Synchronous Get(同步Get)

下載一個文件,打印他的響應頭,以string形式打印響應體。
       響應體的 string() 方法對於小文檔來講十分方便、高效。可是若是響應體太大(超過1MB),應避免適應 string()方法 ,由於他會將把整個文檔加載到內存中。對於超過1MB的響應body,應使用流的方式來處理body。

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

Asynchronous Get(異步Get)

       在一個工做線程中下載文件,當響應可讀時回調Callback接口。讀取響應時會阻塞當前線程。OkHttp現階段不提供異步api來接收響應體。

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(提取響應頭)

典型的HTTP頭 像是一個 Map

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(Post方式提交String)

       使用HTTP POST提交請求到服務。這個例子提交了一個markdown文檔到web服務,以HTML方式渲染markdown。由於整個請求體都在內存中,所以避免使用此api提交大文檔(大於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 Streaming(Post方式提交流)

       以流的方式POST提交請求體。請求體的內容由流寫入產生。這個例子是流直接寫入Okio的BufferedSink。你的程序可能會使用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()); }

Posting a File(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()); }

Posting form parameters(Post方式提交表單)

       使用FormEncodingBuilder來構建和HTML標籤相同效果的請求體。鍵值對將使用一種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()); }

Posting a multipart request(Post方式提交分塊請求)

       MultipartBuilder能夠構建複雜的請求體,與HTML文件上傳形式兼容。多塊請求體中每塊請求都是一個請求體,能夠定義本身的請求頭。這些請求頭能夠用來描述這塊請求,例如他的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()); }

Parse a JSON Response With Gson(使用GSON解析JSON響應)

       Gson是一個在JSON和Java對象之間轉換很是方便的api。這裏咱們用Gson來解析Github API的JSON響應。
注意:ResponseBody.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 Caching(響應緩存)

       爲了緩存響應,你須要一個你能夠讀寫的緩存目錄,和緩存大小的限制。這個緩存目錄應該是私有的,不信任的程序應不能讀取緩存內容。
       一個緩存目錄同時擁有多個緩存訪問是錯誤的。大多數程序只須要調用一次new OkHttp(),在第一次調用時配置好緩存,而後其餘地方只須要調用這個實例就能夠了。不然兩個緩存示例互相干擾,破壞響應緩存,並且有可能會致使程序崩潰。
       響應緩存使用HTTP頭做爲配置。你能夠在請求頭中添加Cache-Control: max-stale=3600 ,OkHttp緩存會支持。你的服務經過響應頭肯定響應緩存多長時間,例如使用Cache-Control: max-age=9600。

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。須要注意的是:若是您使用FORCE_CACHE和網絡的響應需求,OkHttp則會返回一個504提示,告訴你不可知足請求響應。
       Canceling a Call(取消一個Call)
       使用Call.cancel()能夠當即中止掉一個正在執行的call。若是一個線程正在寫請求或者讀響應,將會引起IOException。當call沒有必要的時候,使用這個api能夠節約網絡資源。例如當用戶離開一個應用時。無論同步仍是異步的call均可以取消。
       你能夠經過tags來同時取消多個請求。當你構建一請求時,使用RequestBuilder.tag(tag)來分配一個標籤。以後你就能夠用OkHttpClient.cancel(tag)來取消全部帶有這個tag的call。.

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(超時)

       沒有響應時使用超時結束call。沒有響應的緣由多是客戶點連接問題、服務器可用性問題或者這之間的其餘東西。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(每一個Call的配置)

       使用OkHttpClient,全部的HTTP Client配置包括代理設置、超時設置、緩存設置。當你須要爲單個call改變配置的時候,clone 一個 OkHttpClient。這個api將會返回一個淺拷貝(shallow copy),你能夠用來單獨自定義。下面的例子中,咱們讓一個請求是500ms的超時、另外一個是3000ms的超時。

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會自動重試未驗證的請求。當響應是401 Not Authorized時,Authenticator會被要求提供證書。Authenticator的實現中須要創建一個新的包含證書的請求。若是沒有證書可用,返回null來跳過嘗試。

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:

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: private int responseCount(Response response) { int result = 1; while ((response = response.priorResponse()) != null) { result++; } return result; }

OkHttp官方文檔:https://github.com/square/okhttp/wiki
參考連接:http://www.cnblogs.com/ct2011/p/3997368.html

相關文章
相關標籤/搜索