OkHttp、rxJava、Retrofit聯合網絡請求(一)

如今基本上全部的網絡框架都採用Okhttp、rxjava、retrofit三者一塊兒寫的。由於最近沒有什麼事情,就抽空總結一下這方面的知識:由於這些東西連在一期講的話,不少同窗會以爲懵逼,因此這裏我準備先講一下每個東西的用法,而後在講解一下怎麼聯合使用。html

最近看了 《X特遣隊/自殺小隊》 以爲不錯。以一種混子的心態生活,其實挺輕鬆的!因此一張圖片鎮樓!習慣的我能夠發給你!java

本文知識點

  • OkHttp的使用
  • OkHttp上傳文件
  • OkHttp的一些高級用法

1. OkHttp的簡單使用

在這裏先來個重要的說明:網絡權限必定要加,必定要加!!!git

其實關於OkHttp的使用只要記住一個順序就能夠github

  • 建立OkHttpClient對象
  • 建立請求Request內容
  • 發送請求
  • 建立請求的回調

基本上記住上面的步驟就能夠實現簡單的請求了!chrome

1.1 簡單的GET請求

既然上面都提到了相應的步驟,咱們就按照上面的步驟寫一下就能夠了!!!json

1.1.1 建立OkHttpClient對象

OkHttpClient httpClient = new OkHttpClient();
複製代碼

建立一個對象而已,沒有什麼好說的!!!安全

1.1.2 建立請求Request內容

Request request = new Request.Builder()
                .method("GET", null)
                .url("https://www.baidu.com/")
                .build();
複製代碼

這裏簡單說一下,method是設置相應的請求方式的;url是設置相應的請求地址的!其次Request是一個構建者的構建模式。剩下的沒有什麼好說的。,若是新手,不用管那麼多爲何,實現效果纔是重要的!!!bash

1.1.3 發送請求

Call call = httpClient.newCall(request);
複製代碼

這裏其實就是讓httpClient知道本身要請求什麼而已服務器

1.1.4 建立請求返回的內容

call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            Log.e(TAG, "請求失敗的緣由:" + e);
        }

        @Override
        public void onResponse(Call call, final Response response) throws IOException {
            Headers headers = response.headers();
            Set<String> names = headers.names();
            for (String name : names) {
                Log.e(TAG, "請求的header" + name);
                String value = headers.get(name);
                Log.e(TAG, "值爲: " + value + "\n----------------------------------");
            }

            final String date = response.body().string();
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mTvShow.setText(date);
                }
            });
        }
    });
複製代碼

這裏要說明的就多了:markdown

  1. 當你的看到FATAL EXCEPTION: OkHttp Dispatcher這個異常的時候,恭喜你,你踩到第一個坑了!這個主要是由於response.body().string()只能調用一次,若是你在代碼中調用了兩次,那麼就會出現上面的異常;

  2. 當你異步請求的時候,是不能在子線程修改UI的,因此這裏我用了一個Handler去操做相應的內容

  3. 若是你想看相應的一些內容的話,那麼看那個for循環那裏,你打印一下,就能看到以下的內容,若是不怎麼理解的話,找大家後臺人員請教一下!必定要虛心哦。

    Accept-Ranges →bytes
    Cache-Control →no-cache
    Connection →Keep-Alive
    Content-Length →227
    Content-Type →text/html
    Date →Wed, 05 Sep 2018 03:41:58 GMT
    Etag →"5b7b7f40-e3"
    Last-Modified →Tue, 21 Aug 2018 02:56:00 GMT
    Pragma →no-cache
    Server →BWS/1.1
    Set-Cookie →BD_NOT_HTTPS=1; path=/; Max-Age=300
    Strict-Transport-Security →max-age=0
    X-Ua-Compatible →IE=Edge,chrome=1
    複製代碼
  4. 若是失敗的話,那麼就會在onFailure中把異常反饋給你!!!

給你貼下總體代碼吧!

/*1.建立OkHttpClient對象*/
        OkHttpClient httpClient = new OkHttpClient();
        /*2.建立請求Request內容*/
        Request request = new Request.Builder()
                .method("GET", null)
                .url("https://www.baidu.com/")
                .build();
        /*3.發送請求*/
        Call call = httpClient.newCall(request);
        /*4.建立請求的回調*/
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e(TAG, "請求失敗的緣由:" + e);
            }

            @Override
            public void onResponse(Call call, final Response response) throws IOException {
                Headers headers = response.headers();
                Set<String> names = headers.names();
                for (String name : names) {
                    Log.e(TAG, "請求的header" + name);
                    String value = headers.get(name);
                    Log.e(TAG, "值爲: " + value + "\n----------------------------------");
                }


                final String date = response.body().string();
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mTvShow.setText(date);
                    }
                });
            }
        });
複製代碼

以上步驟就能正常請求相應的數據了,若是尚未數據的話,好好看看代碼!

1.2 簡單的POST請求

關於POST請求的話,基本上就是比GET請求多一步設置表單的方法,也就是一個FormBody對象的設置,以key、value的方式設置表單而已,因此這裏教你怎麼寫,而後我貼一下代碼就那麼滴了,誰讓我那麼懶呢!!!

表單的寫法是這樣的:

FormBody formBody = new FormBody.Builder()
                .add("key", "value")
                .build();
複製代碼

其實add方法能夠被調用屢次,添加相應的key和value;

總體的代碼是這樣的!!!

/*1.建立OkHttpClient對象*/
        OkHttpClient httpClient = new OkHttpClient();
        /*2.建立相應的表單內容*/
        FormBody formBody = new FormBody.Builder()
                .add("key", "value")
                .build();
        /*3.建立請求Request內容*/
        Request request = new Request.Builder()
                .url("https://www.baidu.com/")
                .post(formBody)
                .build();
        /*4.發送請求*/
        Call call = httpClient.newCall(request);
        /*5.建立請求的回調*/
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e(TAG, "請求失敗的緣由:" + e);
            }

            @Override
            public void onResponse(Call call, final Response response) throws IOException {
                Headers headers = response.headers();
                Set<String> names = headers.names();
                for (String name : names) {
                    Log.e(TAG, "請求的header" + name);
                    String value = headers.get(name);
                    Log.e(TAG, "值爲: " + value + "\n----------------------------------");
                }


                final String date = response.body().string();
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mTvShow.setText(date);
                    }
                });
            }
        });
    }
複製代碼

POST和GET請求只是請求的方式不一樣,POST比較安全,全部內容都依靠表單傳遞!

2. OkHttp3進行文件上傳

在這裏先來個重要的說明:去寫SD卡的權限必定要加,必定要加!!!

說到文件上傳,通常的網絡請求都帶有文件上傳的功能,其實OkHttp3也能夠上傳文件,具體操做步驟以下:

  • 建立OkHttpClient對象
  • 建立請求Request內容和全部所需參數(這裏和其餘的請求不一樣的地方)
    • 獲取文件
    • 設置上傳文件的類型
    • 獲取請求體
  • 建立請求
  • 建立請求的回調

由於其餘的內容都差很少,只有關於表單的內容不通,因此這裏着重講一下關於這個表單的問題。

RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("title", "張三")
                .addFormDataPart("image", "zhangsan.jpg", RequestBody.create(MediaType.parse("application/octet-stream"), new File(Environment.getExternalStorageDirectory().getParent() + "/0/123.png")))
                .build();
複製代碼

通常這種上傳文件,基本上都是傳遞相應的用戶圖片,修改圖片什麼的!由於服務器要根據你上傳的這張圖片進行相應圖片的替換。回來講上面那個配置:

  1. 上面第一個"title"那個參數的話,應該是一組key、value形式,基本上是根據服務器定的參數爲準,能夠有多組!
  2. 第二個"image"那個參數的話,基本上就是這個形式的key、圖片名稱、圖片位置。這樣就能鎖定一張圖片了,這樣就構建出一個相應的RequestBody對象了。

總體代碼是這樣的:

/*1.建立OkHttpClient對象*/
        OkHttpClient httpClient = new OkHttpClient();
        /*2.建立相應的表單內容*/
        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("title", "張三")
                .addFormDataPart("image", "zhangsan.jpg", RequestBody.create(MediaType.parse("application/octet-stream"), new File(Environment.getExternalStorageDirectory().getParent() + "/0/123.png")))
                .build();

        /*3.建立請求Request內容*/
        Request request = new Request.Builder()
                .header("key", "value")
                .url("https://www.baidu.com/")
                .post(requestBody)
                .build();

        /*4.發送請求*/
        Call call = httpClient.newCall(request);
        /*5.建立請求的回調*/
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e(TAG, "onFailure: " + e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.e(TAG, "onResponse: " + response.body().string());
            }
        });
複製代碼

對了忘說了一點,圖片是以流的形式進行傳遞的。因此上面"application/octet-stream"配置的是這種格式,若是是其餘的格式呢?給你們一份對照表:參照一下就OK了。

參數 說明
text/html HTML格式
text/plain 純文本格式
text/xml XML格式
image/gif gif圖片格式
image/jpeg jpg圖片格式
image/png png圖片格式
application/xhtml+xml XHTML格式
application/xml XML數據格式
application/atom+xml Atom XML聚合格式
application/json JSON數據格式
application/pdf pdf格式
application/msword Word文檔格式
application/octet-stream 二進制流數據

基本上你把上面的代碼改吧改吧就能上傳文件了!!!就醬紫簡單。。。

3. OkHttp的高端配置

3.1 OkHttp一些基本參數的配置

配置請求時間和鏈接超時的時間等等

OkHttpClient httpClient = new OkHttpClient.Builder()
            //設置相應的鏈接池
            .connectionPool(new ConnectionPool())
            //鏈接超時
            .connectTimeout(15, TimeUnit.SECONDS)
            //寫入超時
            .writeTimeout(15, TimeUnit.SECONDS)
            //讀取超時
            .readTimeout(20, TimeUnit.SECONDS)
            .build();
複製代碼

3.2 OkHttp攔截器的一些簡單理解

每每在項目中,都會有一些關於公共請求參數的一些問題,這裏就會用到相應的OkHttp攔截器!什麼是攔截器呢?簡單點說就和埋點差很少。在請求的時候,會走每個攔截器!想添加什麼就添加什麼,這裏咱們經過幾個實例講解一下你就能大概理解了!

3.2.1 日誌攔截器

先看下代碼,而後我在作一下相應的解釋:

public class LogInterceptor implements Interceptor {

    private static final String TAG = LogInterceptor.class.getSimpleName();

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        
        /*這樣就能在請求以前打印相應的內容了*/
        Log.e("url", String.format("Sending request %s on %s %n %s", request.url(), chain.connection(), request.headers()));

        /*其實下面這個chain.proceed(request)這個方法,表明請求前和請求後*/
        return chain.proceed(request);
    }
}
複製代碼

這裏就是直接打印了一個相應的LOG,能夠獲取到一些請求的參數,這裏說明一下:

  1. 當你的請求爲GET請求的時候只能打印一個Url地址,也就是request.url()的值了,飲後後面的headers獲取到的內容爲空,由於GET請求沒有相應的表單信息;
  2. chain.connection()當你使用除了日誌攔截器的時候,就會返回空
  3. chain.proceed(request)表明請求響應的結果,因此說明你也是能夠修改返回結果的!!!

3.2.2 重定向一個網址連接的攔截器

這個說來就有意思了,當你請求攔截器的時候,正常應該返回百度返回的內容,可是若是你修改了連接的地址會怎麼樣呢?固然就會返回你修改以後的返回地址了。。。咱們看看怎麼實現的

public class ResetInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {

        Request newRequest = new Request.Builder()
                .method("GET", null)
                .url("https://fanyi.baidu.com/translate?aldtype=16047&query=%E8%BF%9B%E5%BA%A6%0D%0A&keyfrom=baidu&smartresult=dict&lang=auto2zh#zh/en/%E9%87%8D%E7%BD%AE")
                .build();

        return chain.proceed(newRequest);
    }
}
複製代碼

對,你沒有看錯,就這麼赤裸裸的換了一個url地址,其實Request request = chain.request();這個方法,返回的Request就是在建立的時候,建立的Request,因此,這裏你直接,經過攔截器,直接建立一個新的,直接返回就能夠了,就沒有以前的Request什麼事情了!!!其實就至關於你把以前的內容從新寫了一遍!就醬紫了。。。

3.2.3 添加相應的公共請求參數

其實這個的實現和上面的差很少,也就是替換相應的Request的內容!可是這裏你要考慮一個問題,就是GET請求和POST請求的處理方式應該是不一樣的,多以這裏要分狀況去處理。不然不能達到你想要的效果的!因此這裏咱們分開說。先說明一下,GET請求是在Url後面拼接相應的參數,而POST請求是在form表單中添加相應的參數,因此方式必定是不同的!!!

1. GET請求添加公共請求參數

先來一段代碼體驗一下:

HttpUrl build = originalRequest.url().newBuilder()
            .addQueryParameter("key1", "value1")
            .addQueryParameter("key2", "value2")
            .addQueryParameter("key3", "value3")
            .addQueryParameter("key4", "value4")
            .addQueryParameter("key5", "value5")
            .build();

    Request request = originalRequest.newBuilder().url(build).build();
複製代碼

這樣就能夠添加相應的公共請求參數了,其實開始的時候,我覺得newBuilder()是建立一個新的內容呢?其實它是拿到以前的內容,而後把下面的內容添加進去。因此這裏其餘的內容是不會收到影響的!!!

其實GET請求就是在URL後面追加上相應的參數。

2. POST請求添加公共請求參數

仍是先來一點代碼體驗一下:

Request requestBuilder = originalRequest.newBuilder()
        .addHeader("key1", "value1")
        .addHeader("key2", "value2")
        .addHeader("key3", "value3")
        .addHeader("key4", "value4")
        .addHeader("key5", "value5")
        .build();
複製代碼

和上面的相似,只是寫法不一樣而已!由於POST請求添加的是相應的header。

總體的代碼以下:

public class PublicInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {

        Request request = chain.request();

        if ("GET".equals(request.method())) {
            //GET請求的處理
            HttpUrl build = request.url().newBuilder()
                    .addQueryParameter("key1", "value1")
                    .addQueryParameter("key2", "value2")
                    .addQueryParameter("key3", "value3")
                    .addQueryParameter("key4", "value4")
                    .addQueryParameter("key5", "value5")
                    .build();

            request = request.newBuilder().url(build).build();
        } else if ("POST".equals(request.method())) {
            request = request.newBuilder()
                    .addHeader("key1", "value1")
                    .addHeader("key2", "value2")
                    .addHeader("key3", "value3")
                    .addHeader("key4", "value4")
                    .addHeader("key5", "value5")
                    .build();
        }

        return chain.proceed(request);
    }
}
複製代碼

最後在把相應的Interceptor添加到OkHttp就行了。


2018年10月15日補充:

在POST請求中,請求參數應該添加到body中,因此上面代碼是有問題的!

替換成下面這樣:

if (originalRequest.body() instanceof FormBody) {
            // 構造新的請求表單
            FormBody.Builder builder = new FormBody.Builder();

            FormBody body = (FormBody) originalRequest.body();
            //將之前的參數添加
            for (int i = 0; i < body.size(); i++) {
                builder.add(body.encodedName(i), body.encodedValue(i));
            }
            //追加新的參數
            builder.add("key1", "value1");
            builder.add("key2", "value2");
            builder.add("key3", "value3");
            builder.add("key4", "value4");
            builder.add("key5", "value5");
            //構造新的請求體
            originalRequest = originalRequest.newBuilder().post(builder.build()).build();
        }
複製代碼

對於以上的錯誤深表歉意,由於沒有弄清楚http中的一下內容,還請見諒!!!


基本上使用的時候就這麼多問題,可能有些講解不到的,若是有什麼不到位的,及時補充!!!有問題留言,我看到了必定會回覆你的!!!

github地址奉上

相關文章
相關標籤/搜索