文章目錄
- 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 P系統的裝置上,如果應用使用的是非加密的明文流量的http網絡請求,則會導緻該應用無法進行網絡請求,https則不會受影響,同理若應用内使用WebView加載網頁 則加載網頁也需要是https請求。
解決方法:
- APP整體網絡請求改用https
- 将targetSdkVersion 版本下調至27以下
- 更改項目網絡安全配置
三種方法亦可,這裡主要介紹一下第三種解決方法,以拓寬解決思路
- 在res目錄下建立xml檔案夾 在xml檔案夾内建立名為
,代碼如下:network_config(名字非固定)的xml
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
- 然後,在AndroidManifest中增加
屬性,代碼如下:android:networkSecurityConfig
<application
...
android:networkSecurityConfig="@xml/network_security_config"
...
/>
- 以上兩個步驟就完成了網絡安全配置,如果實在不行的話可以嘗試另外兩種方法或者百度
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對象;
- 通過
方法來送出同步請求。注意,由于是同步請求,是以該api要放在子線程中,避免阻塞主線程,造成ANR異常。另外,Android3.0 以後已經不允許在主線程通路網絡。call.execute()
代碼如下:
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();
}
});
}
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());
}
});
}
});
}
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();
}
});
}
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());
}
});
}
});
}
4 總結
- 同步調用:編寫簡單,但是會阻塞主線程,一般不适用
- 異步調用:回調函數是在子線程,我們不能在子線程更新UI,需要借助于runOnUiThread()方法或者Handler來處理