1. 程式人生 > >Java EE 架構設計——基於okhttp3 的網絡框架設計

Java EE 架構設計——基於okhttp3 的網絡框架設計

知識 tap 使用 pass web asc 封裝 useragent 占用

轉載請註明出處: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 的網絡框架設計