天天看點

OkHttp3 使用詳解

一,簡介

OkHttp 是一個高效的 HTTP 用戶端,具有非常多的優勢:

  1. 能夠高效的執行 http,資料加載速度更快,更省流量
  2. 支援 GZIP 壓縮,提升速度,節省流量
  3. 緩存響應資料,避免了重複的網絡請求
  4. 使用簡單,支援同步阻塞調用和帶回調的異步調用

OkHttp 支援 Android2.3 以上,JDK1.7 以上。

官網位址:https://square.github.io/okhttp/

github位址:https://github.com/square/okhttp

二,基本用法

添加 OkHttp 依賴

compile 'com.squareup.okhttp3:okhttp:(insert latest version)'

最新的版本号可以在官網和github上找到

1. Get 請求

使用 OkHttp 進行 Get 請求隻需要四個步驟

  1. 建立 OkHttpClient對象
  2. 構造 Request 對象
  3. 将 Request 對象封裝為 Call
  4. 通過 Call 來執行同步或異步請求
String url = "http://www.xxxx.com";
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(url)
                .get()  //預設為GET請求,可以不寫
                .build();
       final Call call = client.newCall(request);
           

1.1 Get 同步請求

通過 call.excute() 方法來送出同步請求,這種方式會阻塞線程,而為了避免 ANR 異常,Android3.0 之後已經不允許在主線程中通路網絡了

是以 OkHttp 的同步 get 請求需要開啟一個子線程:

new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Response response = call.execute();
                    Log.d(TAG, response.body().toString());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
           

1.2 Get 異步請求

通過 call.enqueue(Callback)方法來送出異步請求

call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d(TAG, "onFailure: " + e);
            }
 
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.d(TAG, "OnResponse: " + response.body().toString());
            }
        });
           

2. Post 請求

建立 Post 請求的方式與 Get 方法類似,隻是需要增加一個步驟,構造出一個請求參數對象RequestBody ,用于攜帶我們需要送出的資料。(下面均以 Post 的異步請求為例,Post 同步請求隻需将 call.enqueue() 替換成 call.execute() 即可)

public Builder post(RequestBody body)
           

Request 的 post 方法所接收的參數是 RequestBody 對象,是以隻要是 RequestBody 類及其子類都可以當做參數傳入。

RequestBody是一個抽象類,常用的 RequestBody 實作類有這麼幾種:

2.1 FormBody

FormBody是RequestBody的實作類,用于表單方式的請求

OkHttpClient client = new OkHttpClient();
        //建立表單請求參數
        FormBody.Builder builder = new FormBody.Builder();
        builder.add("name", "zhangsan");
        builder.add("age", "18");
        FormBody formBody = builder.build();
        Request request = new Request.Builder()
                .url(url)
                .post(formBody)
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
            }
        });
           

2.2 RequestBody.create(...)

RequestBody 是一個抽象類,我們不能直接使用它,但是可以通過調用它的靜态create方法來擷取一個RequestBody對象,該方法會建立并傳回一個 RequestBody 的匿名内部類執行個體

檢視一下 RequestBody 類,發現它有這樣幾個 create 方法。

OkHttp3 使用詳解

其中前三個方法最終調用的都是第四個方法,是以我們可以具體看一下最後兩個方法的具體實作

/** Returns a new request body that transmits {@code content}. */
  public static RequestBody create(final @Nullable MediaType contentType, final byte[] content,
      final int offset, final int byteCount) {
    if (content == null) throw new NullPointerException("content == null");
    Util.checkOffsetAndCount(content.length, offset, byteCount);
    return new RequestBody() {
      @Override public @Nullable MediaType contentType() {
        return contentType;
      }
      @Override public long contentLength() {
        return byteCount;
      }
      @Override public void writeTo(BufferedSink sink) throws IOException {
        sink.write(content, offset, byteCount);
      }
    };
  }
  /** Returns a new request body that transmits the content of {@code file}. */
  public static RequestBody create(final @Nullable MediaType contentType, final File file) {
    if (file == null) throw new NullPointerException("content == null");
    return new RequestBody() {
      @Override public @Nullable MediaType contentType() {
        return contentType;
      }
      @Override public long contentLength() {
        return file.length();
      }
      @Override public void writeTo(BufferedSink sink) throws IOException {
        Source source = null;
        try {
          source = Okio.source(file);
          sink.writeAll(source);
        } finally {
          Util.closeQuietly(source);
        }
      }
    };
  }
           

這裡多解釋一下

Content-Type(MediaType),即是Internet Media Type,網際網路媒體類型;也叫做MIME類型,在Http協定消息頭中,使用Content-Type來表示具體請求中的媒體類型資訊。用于定義網絡檔案的類型和網頁的編碼,決定檔案接收方将以什麼形式、什麼編碼讀取這個檔案。常見的媒體格式類型有:

  • text/html:HTML格式
  • text/pain:純文字格式
  • image/jpeg:jpg圖檔格式
  • application/json:JSON資料格式
  • application/octet-stream:二進制流資料(如常見的檔案下載下傳)
  • application/x-www-form-urlencoded:form表單encType屬性的預設格式,表單資料将以key/value的形式發送到服務端
  • multipart/form-data:表單上傳檔案的格式

使用 create 方法可以用來用于上傳 String 和 File 對象,具體實作如下:

上傳JSON字元串:

OkHttpClient client = new OkHttpClient();
        //指定目前請求的 contentType 為 json 資料
        MediaType JSON = MediaType.parse("application/json; charset=utf-8");
        String jsonStr = "{\"name\":\"zhangsan\",\"age\":\"20\"}";
        Request request = new Request.Builder()
                .url(url)
                .post(RequestBody.create(JSON, jsonStr))
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
            }
        });
           

上傳檔案:

OkHttpClient client = new OkHttpClient();
        File file = new File(filePath);
        Request request = new Request.Builder()
                .url(url)
                .post(RequestBody.create(MediaType.parse("application/octet-stream"), file))
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
            }
        });
           

2.3 使用MultipartBody同時上傳多種類型資料

多檔案和鍵值對同時上傳

OkHttpClient client = new OkHttpClient();
        MultipartBody multipartBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("name", "zhangsan")
                .addFormDataPart("age", "20")
                .addFormDataPart("file", file.getName(),
                        RequestBody.create(MediaType.parse("application/octet-stream"), file))
                .build();
        
        Request request = new Request.Builder()
                .url(url)
                .post(multipartBody)
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
            }
        });
           

3. Call 請求器

OkHttp 用戶端負責接收應用程式發出的請求,并且從伺服器擷取響應傳回給應用程式。理論聽起來十分簡單,但是在實踐中往往會出現很多意想不到的問題。

通過配置 OkHttpClient,可以配置重寫請求、重寫響應、跟蹤請求、重試請求等多種操作,這樣一來你發送的一個簡單請求可能就會變成需要發送多個請求以及接收多個響應後才能獲得想要的響應。OkHttp 将這些多次的中間請求和響應任務模組化成了一個 Call 對象,但是通常情況下中間請求及響應工作不會很多,令人欣慰的是,無論發生URL重定向還是因為伺服器出現問題而向一個備用IP位址再次發送請求的情況,你的代碼都将正常運作。

執行Call有兩種方式:

  • 同步:請求和處理響應發生在同一線程。并且此線程會在響應傳回之前會一直被堵塞。
  • 異步:請求和處理響應發生在不同線程。将發送請求操作發生在一個線程,并且通過回調的方式在其他線程進行處理響應。(一般在子線程發送請求,主線程處理響應)。

Calls可以在任何線程被取消。當這個Call尚未執行結束時,執行取消操作将會直接導緻此Call失敗!當一個Call被取消時,無論是寫入請求主體或者讀取響應主體的代碼操作,都會抛出一個IOException異常。