天天看點

每天學習一個Android中的常用架構——3.OkHttp

文章目錄

  • ​​1.簡介​​
  • ​​2.特性​​
  • ​​3.示範​​
  • ​​3.1 內建​​
  • ​​3.2 配置​​
  • ​​3.3 布局檔案和URL封裝​​
  • ​​3.4 同步GET請求​​
  • ​​3.5 異步GET請求​​
  • ​​3.6 同步POST請求​​
  • ​​3.7 異步POST請求​​
  • ​​4 總結​​
  • ​​5.源碼位址​​

1.簡介

在過去,Android上發送HTTP請求一般有兩種方式:HttpURLConnection和HttpClient(Apache HttpClient)。不過由于HttpClient存在API數量過多、擴充困難等缺點,Android團隊越來越不建議我們使用這種方式。終于在Android 6.0系統中,HttpClient的功能被完全移除了,标志着此功能被正式棄用。雖然官方推薦使用HttpURLConnection,但是在代碼編寫的過程中仍然會比較繁瑣。此時,一個将要替代它們的網絡層架構出現了:也就是Okhttp,該架構也是我在《Android 第一行代碼》上學到的僅此于Litepal之後的第二個架構。今天重新學習了一下Okhttp,将學習曆程記錄到部落格上,望對讀者有幫助。

OkHttp是由鼎鼎大名的Square公司開發的,這個公司在開源事業上面貢獻良多,除了 OkHttp 之外,還開發了像Picasso,Retrofit等著名的開源項目。OkHttp不僅在接口封裝上面做得簡單易用,就連在底層實作上也是自成一派,比起原生的HttpURLConnection,可以說是有過之而無不及,現在已經成了廣大Android開發者首選的網絡通信庫。

2.特性

OkHttp是一個高效的HTTP用戶端,它有以下預設特性:

  • 支援HTTP/2,允許所有同一個主機位址的請求共享同一個socket連接配接
  • 連接配接池減少請求延時
  • 透明的GZIP壓縮減少響應資料的大小
  • 緩存響應内容,避免一些完全重複的請求

當網絡出現問題的時候OkHttp依然堅守自己的職責,它會自動恢複一般的連接配接問題,如果你的服務有多個IP位址,當第一個IP請求失敗時,OkHttp會交替嘗試你配置的其他IP,OkHttp使用現代TLS技術(SNI, ALPN)初始化新的連接配接,當握手失敗時會回退到TLS 1.0。

OkHttp 支援 Android 2.3 及以上版本Android平台, 以及 Java,JDK 1.7及以上.

OkHttp的使用是非常簡單的. 它的請求/響應 API 使用構造器模式builders來設計,它支援阻塞式的同步請求和帶回調的異步請求。

為了更好地示範OkHttp,這裡僅展示出同步/異步執行get/post請求的相關api,至于關于OkHttp的相關調用,建議參考OkHttp的官方文檔:​​​OkHttp官網,無需翻牆​​ 關于使用post請求發送流/檔案/表單等,這些功能則要配合另一個架構:OkIo,将在下一篇部落格中專門展示出來。

話不多說,讓我們馬上開始使用OkHttp吧。

3.示範

3.1 內建

又到了喜聞樂見的內建環節,我們隻需要修改module下的build.gradle,加入OkHttp的依賴,代碼如下:

implementation("com.squareup.okhttp3:okhttp:4.6.0")      

記得重新Sync一下,確定OkHttp內建到了你的項目中。作者在寫下此篇部落格時OkHttp3的穩定版本号以達到​

​4.6.0​

​,如果讀者在OkHttp的官網上發現了更新的版本,記得及時更新,做到與時俱進。

3.2 配置

既然你內建了OkHttp,就不可不免地要進行與網絡有關的操作。既然如此,就需要在清單檔案下聲明網絡權限,即:

<uses-permission android:name="android.permission.INTERNET" />      

另外:如果你的Android版本為Android P(即targetSdkVersion 27),在運作該項目時可能會報有關網絡的錯誤,如圖所示:

每天學習一個Android中的常用架構——3.OkHttp

原因:在Android P系統的裝置上,如果應用使用的是非加密的明文流量的http網絡請求,則會導緻該應用無法進行網絡請求,https則不會受影響,同理若應用内使用WebView加載網頁 則加載網頁也需要是https請求。

解決方法:

  • APP整體網絡請求改用https
  • 将targetSdkVersion 版本下調至27以下
  • 更改項目網絡安全配置

三種方法亦可,這裡主要介紹一下第三種解決方法,以拓寬解決思路

  1. 在res目錄下建立xml檔案夾 在xml檔案夾内建立名為​

    ​network_config(名字非固定)的xml​

    ​,代碼如下:
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>      
  1. 然後,在AndroidManifest中增加​

    ​android:networkSecurityConfig​

    ​屬性,代碼如下:
<application
    ...
     android:networkSecurityConfig="@xml/network_security_config"
    ...
        />      
  1. 以上兩個步驟就完成了網絡安全配置,如果實在不行的話可以嘗試另外兩種方法或者百度

3.3 布局檔案和URL封裝

接下來,我們直接開始布局檔案activity_main.xml的編寫。該布局很簡單,僅有四個按鈕,代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_get_syn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="發送get請求——同步"/>

    <Button
        android:id="@+id/btn_get_asy"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="發送get請求——異步"/>

    <Button
        android:id="@+id/btn_post_syn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="發送post請求——異步"/>

    <Button
        android:id="@+id/btn_post_asy"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="發送post請求——異步"/>

</LinearLayout>      

之後,我們簡單用字元串封裝一下要請求的URL,即​

​http://wwww.baidu.com​

​。這裡建議想要深入OkHttp的讀者自己搭建一個Tomcat/Apache伺服器,将資源檔案上傳到伺服器上,然後通路一下本地路徑,這樣可以加深對于網絡請求的了解。這裡為了便于示範就直接網絡請求百度,代碼如下:

private static final String URL = "http://wwww.baidu.com";      

3.4 同步GET請求

同步GET請求的步驟很簡單,大抵分為:

  • 構造OkHttpClient對象;
  • 構造Request對象;
  • 通過前兩步中的對象建構Call對象;
  • 通過​

    ​call.execute()​

    ​方法來送出同步請求。注意,由于是同步請求,是以該api要放在子線程中,避免阻塞主線程,造成ANR異常。另外,Android3.0 以後已經不允許在主線程通路網絡。

代碼如下:

private void getBySynchronized() {
        btn_get_syn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 1.建構Client對象
                OkHttpClient client = new OkHttpClient();
                // 2.采用建造者模式和鍊式調用建構Request對象
                final Request request = new Request.Builder()
                        .url(URL) // 請求URL
                        .get() // 預設就是get請求,可以不寫
                        .build();
                // 3.通過1和2産生的Client和Request對象生成Call對象
                final Call call = client.newCall(request);
                // 4.同步發送get請求需要使用execute()方法,并且為了防止主線程阻塞需要放在子線程中自行
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            // 6.建構response對象
                            Response response = call.execute();
                            Log.d(TAG, "同步發送get請求成功!請求到的資訊為:" + response.body().string());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        });
    }      
每天學習一個Android中的常用架構——3.OkHttp

3.5 異步GET請求

異步GET請求的前面幾個步驟和同步方式一樣,隻是最後一部是通過​

​call.enqueue(Callback())​

​ 來送出請求,代碼如下:

private void getByAsynchronized() {
        btn_get_asy.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 1.建構Client對象
                OkHttpClient client = new OkHttpClient();
                // 2.采用建造者模式和鍊式調用建構Request對象
                final Request request = new Request.Builder()
                        .url(URL) // 請求URL
                        .get() // 預設就是get請求,可以不寫
                        .build();
                // 3.通過1和2産生的Client和Request對象生成Call對象
                Call call = client.newCall(request);
                // 4.調用Call對象的enqueue()方法,并且實作一個回調實作類
                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(@NotNull Call call, @NotNull IOException e) {
                        Log.d(TAG, "異步發送get請求失敗!");
                        e.printStackTrace();
                    }

                    @Override
                    public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                        Log.d(TAG, "異步發送get請求成功!請求到的資訊為:" + response.body().string());
                    }
                });
            }
        });
    }      
每天學習一個Android中的常用架構——3.OkHttp

3.6 同步POST請求

同步POST請求的步驟類似于同步GET請求,大抵分為:

  • 構造OkHttpClient對象;
  • 構造FormBody對象(鍵值對)
  • 構造Request對象;
  • 通過前兩步中的對象建構Call對象;
  • 通過​

    ​call.execute()​

    ​方法來送出同步請求。

代碼如下:

private void postBySynchronized() {
        btn_post_syn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 1.建構Client對象
                OkHttpClient client = new OkHttpClient();
                // 2.采用建造者模式和鍊式調用建構鍵值對對象
                FormBody formBody = new FormBody.Builder()
                        .add("username", "admin")
                        .add("password", "123456")
                        .build();
                // 3.采用建造者模式和鍊式調用建構Request對象
                final Request request = new Request.Builder()
                        .url(URL) // 請求URL
                        .post(formBody) // 預設就是get請求,可以不寫
                        .build();
                // 4.通過1和3産生的Client和Request對象生成Call對象
                final Call call = client.newCall(request);
                // 5.同步發送post請求需要使用execute()方法,并且為了防止主線程阻塞需要放在子線程中自行
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            // 6.建構response對象
                            Response response = call.execute();
                            Log.d(TAG, "同步發送post請求成功!請求到的資訊為:" + response.body().string());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        });
    }      
每天學習一個Android中的常用架構——3.OkHttp

3.7 異步POST請求

異步POST請求的前面幾個步驟和同步方式一樣,隻是最後一部是通過​

​call.enqueue(Callback())​

​ 來送出請求,代碼如下:

private void postByAsynchronized() {
        btn_post_asy.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 1.建構Client對象
                OkHttpClient client = new OkHttpClient();
                // 2.采用建造者模式和鍊式調用建構鍵值對對象
                FormBody formBody = new FormBody.Builder()
                        .add("username", "admin")
                        .add("password", "123456")
                        .build();
                // 3.采用建造者模式和鍊式調用建構Request對象
                final Request request = new Request.Builder()
                        .url(URL) // 請求URL
                        .post(formBody) // 預設就是get請求,可以不寫
                        .build();
                // 4.通過1和3産生的Client和Request對象生成Call對象
                Call call = client.newCall(request);
                // 5.調用Call對象的enqueue()方法,并且實作一個回調實作類
                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(@NotNull Call call, @NotNull IOException e) {
                        Log.d(TAG, "異步發送post請求失敗!");
                        e.printStackTrace();
                    }

                    @Override
                    public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                        Log.d(TAG, "異步發送post請求成功!請求到的資訊為:" + response.body().string());
                    }
                });
            }
        });
    }      
每天學習一個Android中的常用架構——3.OkHttp

4 總結

  • 同步調用:編寫簡單,但是會阻塞主線程,一般不适用
  • 異步調用:回調函數是在子線程,我們不能在子線程更新UI,需要借助于runOnUiThread()方法或者Handler來處理

5.源碼位址

繼續閱讀