Java EE 架構設計——基於okhttp3 的網絡框架設計
轉載請註明出處:http://blog.csdn.net/smartbetter/article/details/77893903
本篇文章帶大家設計一套滿意業務需求、代碼健壯高效(高內聚低耦合)並且可拓展的網絡框架。以最新的okhttp3為基礎設計出高效可靠的網絡緩存、多線程文件下載等架構模塊。從此不局限於使用別人的框架,而步入了設計框架,讓自己可以走的更遠,我覺得這才是一名合格開發者所應該具備的能力。在開發中,選擇一個開源框架的標準有很多,例如學習成本、文檔是否齊全、github星數量、現在是否有人維護、流行程度、代碼設計是否有借鑒性、代碼體積等。
首先看一下當前主流網絡框架對比:
網絡框架 | 說明 | 開源地址 |
---|---|---|
okhttp | 適用於Android和Java應用程序的HTTP+HTTP/2客戶端 | https://github.com/square/okhttp |
retrofit | 在okhttp之上做了相應封裝;解耦性,註解處理,簡化代碼;支持上傳和下載文件;支持自動更換解析方式;支持多種http請求庫 | https://github.com/square/retrofit |
Http網絡框架設計圖:
現在很多開源框架都是使用okhttp做底層協議的網絡請求。所以我們適用時代潮流,使用okhttp做網絡請求,而不是用傳統的HttpURLConnection。
1.http協議
http協議,超文本傳輸協議,目前使用最普遍的還是http1.1版本,不過我們也來了解下http2.0協議:
http1.1的特點:
1)支持客戶/服務器模式
2)簡單快速,GET、POST(http的幾種請求方式:Get、POST、HEAD、PUT、DELETE、TRACE、CONNECT、OPTIONS。)
3)靈活,允許傳輸任意類型的數據對象。
4)無連接,限制每次連接只處理一個請求。
5)無狀態,協議對於事務處理沒有記憶能力。
http2.0對比http1.1增加的特點:
1)多路復用允許同時通過單一的HTTP/2連接發起多重的請求-響應消息,單連接多資源的方式減少服務端的鏈接壓力,內存占用更少,連接吞吐量更 大,由於TCP連接的減少而使網絡擁塞狀況得以改善,同時TCP慢啟動時間的減少,使擁塞和丟包恢復速度更快; 2)頭部壓縮,每次都要傳輸UserAgent、Cookie這類不會變動的內容,使用HPACK算法進行壓縮; 3)對請求劃分優先級; 4)服務器推送流(即Server Push技術)。
http請求返回的一些響應碼:
100-101:信息提示; 200-206:成功; 300-305:重定向; 400-415:客戶端錯誤; 500-505:服務器錯誤。
2.網絡框架的基石 okhttp3
需要添加 Maven 依賴:
<!-- okhttp3 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.9.0</version>
</dependency>
關於okhttp的使用方法官方文檔中已經給出了說明:http://square.github.io/okhttp/,下面我們將對okhttp做出補充說明。
1.okhttp的同步請求和異步請求
同步請求(使用 JUnit 編寫測試方法):
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
System.out.println(response.body().string());
}
} catch (IOException e) {
e.printStackTrace();
}
異步請求:
System.out.println(Thread.currentThread().getId());
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).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 {
if (response.isSuccessful()) {
System.out.println(Thread.currentThread().getId());
}
}
});
2.okhttp請求頭和響應頭的實際應用
請求頭可參考:http://tools.jb51.net/table/http_header
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://www.baidu.com")
.addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) " +
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36")
.addHeader("Range", "bytes=2-")
.addHeader("Accept-Encoding", "identity")
.build();
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
Headers headers = response.headers();
for (int i = 0; i < headers.size(); i++) {
System.out.println(headers.name(i) + " : " + headers.value(i));
}
}
} catch (IOException e) {
e.printStackTrace();
}
3.okhttp get請求和post請求
get用於信息獲取,而且應該是安全的和冪等的。
請求 | 特點 |
---|---|
get | get用於信息獲取,而且應該是安全的和冪等的,所謂安全的意味著該操作用於獲取信息而非修改信息,不會影響資源的狀態。冪等意味著對同一URL的多個請求應該返回同樣的結果。 |
post | post用於修改服務器上的資源,需要註意的是post必須要到Form(表單)。 |
get請求:
OkHttpClient client = new OkHttpClient();
HttpUrl httpUrl = HttpUrl.parse("http://localhost:8080/web/HelloServlet")
.newBuilder()
.addQueryParameter("username", "user")
.addQueryParameter("password", "user123")
.build();
Request request = new Request.Builder().url(httpUrl.toString()).build();
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
System.out.println(response.body().string());
}
} catch (IOException e) {
e.printStackTrace();
}
post請求:
默認地,表單數據會編碼為”application/x-www-form-urlencoded”。就是說,在發送到服務器之前,所有字符都會進行編碼,空格轉換為”+”,特殊符號轉換為ASCII HEX值。
除此之外,還有一個multipart/form-data,它不對字符編碼,在使用包含文件上傳的控件的表單時,必須使用該值,例如拍照上傳等。okhttp已經對multipart/form-data進行了相應的封裝,簡化了很多操作。
OkHttpClient client = new OkHttpClient();
FormBody body = new FormBody.Builder()
.add("username", "user") //如果包含中文需要調用addEncoded方法進行轉碼,而不是add方法
.add("age", "18")
.build();
Request request = new Request.Builder().url("http://localhost:8080/web/HelloServlet").post(body).build();
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
System.out.println(response.body().string());
}
} catch (IOException e) {
e.printStackTrace();
}
3.okhttp上傳文件
1.mutipart協議上傳文件
自己實現上傳文件的功能相對又復雜。第一,你需要了解文件上傳之後http請求過程當中的具體協議的含義是什麽,因為你需要自己去封裝中間的一些參數信息;第二,你需要自己去搭建一個web服務。
後端接口可以參照 http://blog.csdn.net/smartbetter/article/details/52056377 中使用FileUpload實現文件上傳部分。
2.okhttp使用mutipart協議上傳文件
RequestBody imageBody = RequestBody.create(MediaType.parse("image/jpeg"),
new File("/Users/macos/Desktop/temp.jpg"));
MultipartBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("name", "zhangsan")
.addFormDataPart("filename", "temp.jpg", imageBody)
.build();
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().
url("http://192.168.1.20:8080/example/upload/api/file").post(body).build();
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
System.out.println(response.body().string());
}
} catch (IOException e) {
e.printStackTrace();
}
4.okhttp數據緩存
1.http協議當中緩存的原理和關鍵字段
緩存關鍵字段:
Expires:指示響應內容過期的時間,格林威治時間GMT
Catche-Control:
| no-cache:無緩存指令
| max-age:如果緩存只是用來和服務器做驗
| only-if-cached:有時你會想顯示可以立即
| max-stale:設置最長過期時間來允許過期的response響應(有時候過期的response比沒有response更好)。
Last-Modified:判斷客戶端數據和服務端數據有沒有變化。如果相同表示沒有人修改,如果修改則Last-Modified事件發生變化。
ETag:對服務器返回的整個response做了相應的編碼處理,得到了一個加密的值。
Date:
If-Modified-Since:請求頭中標識的,客戶端存取的該資源最後一次修改的時間,同Last-Modified。
If-None-Match:請求頭中標識的。
2.okhttp實現數據緩存
okhttp緩存相關類:CacheInterceptor、CacheStrategy、Cache、DiskLruCache。
int maxCacheSize = 10 * 1024 * 1024;
Cache cache = new Cache(new File("/Users/macos/Desktop/source"), maxCacheSize);
OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();
Request request = new Request.Builder().url("http://www.163.com/")
.cacheControl(new CacheControl.Builder().maxStale(365, TimeUnit.DAYS).build())
.build();
Response response = client.newCall(request).execute();
String body1 = response.body().string();
System.out.println("network response " + response.networkResponse());
System.out.println("cache response " + response.cacheResponse());
System.out.println("**************************");
Response response1 = client.newCall(request).execute();
String body2 = response1.body().string();
System.out.println("network response " + response1.networkResponse());
System.out.println("cache response " + response1.cacheResponse());
我們運行這個測試類,顯示如下,可以看到,先請求了network,後請求了cache:
Java EE 架構設計——基於okhttp3 的網絡框架設計