曾經在代碼裏放蕩不羈,現在在博文中日夜兼行,只爲今天與你分享成果。若是以爲本文有用,記得關注我,我將帶給你更多。
介紹
HttpUtils 是近期開源的對 OkHttp 輕量封裝的框架,它首創的異步預處理器,特點的標籤,靈活的上傳下載進度監聽與過程控制功能,在輕鬆解決不少本來使人頭疼問題的同時,設計上也力求純粹與優雅。java
- 鏈式調用,一點到底
- BaseURL、URL佔位符、JSON自動封裝與解析
- 同步攔截器、異步預處理器、回調執行器
- 文件上傳下載(過程控制、進度監聽)
- TCP鏈接池、Http2
項目地址 Gitee:https://gitee.com/ejlchina-zhxu/httputils GitHub:https://github.com/ejlchina/httputilsgit
安裝教程
Maven
<dependency> <groupId>com.ejlchina</groupId> <artifactId>httputils</artifactId> <version>2.2.0</version> </dependency>
Gradle
compile 'com.ejlchina:httputils:2.2.0'
使用說明
1 簡單示例
1.1 構建 HTTP
HTTP http = HTTP.builder().build();
以上代碼構建了一個最簡單的HTTP
實例,它擁有如下三個方法:github
async(String url)
開始一個異步請求sync(String url)
開始一個同步請求cancel(String tag)
根據標籤批量取消請求
爲了使用方便,在構建的時候,咱們更願意指定一個BaseUrl
(請參見下文[5.1 設置 BaseUrl]):json
HTTP http = HTTP.builder() .baseUrl("http://api.demo.com") .build();
爲了簡化文檔,下文中出現的http
均是在構建時設置了BaseUrl
的HTTP
實例。api
1.2 同步請求
使用方法sync(String url)
開始一個同步請求:數組
List<User> users = http.sync("/users") // http://api.demo.com/users .get() // GET請求 .getBody() // 獲取響應報文體 .toList(User.class); // 獲得目標數據
方法sync
返回一個同步HttpTask
,可鏈式使用。緩存
1.3 異步請求
使用方法async(String url)
開始一個異步請求:網絡
http.async("/users/1") // http://api.demo.com/users/1 .setOnResponse((HttpResult result) -> { // 獲得目標數據 User user = result.getBody().toBean(User.class); }) .get(); // GET請求
方法async
返回一個異步HttpTask
,可鏈式使用。框架
2 請求方法(GET|POST|PUT|DELETE)
同步與異步的HttpTask
都擁有get
、post
、put
與delete
方法。不一樣的是:同步HttpTask
的這些方法返回一個HttpResult
,而異步HttpTask
的這些方法返回一個HttpCall
。異步
HttpResult res1 = http.sync("/users").get(); // 同步 GET HttpResult res2 = http.sync("/users")post(); // 同步 POST HttpResult res3 = http.sync("/users/1").put(); // 同步 PUT HttpResult res4 = http.sync("/users/1").delete();// 同步 DELETE HttpCall call1 = http.async("/users").get(); // 異步 GET HttpCall call2 = http.async("/users").post(); // 異步 POST HttpCall call3 = http.async("/users/1").put(); // 異步 PUT HttpCall call4 = http.async("/users/1").delete();// 異步 DELETE
3 解析請求結果
3.1 回調函數
只有異步請求才能夠設置回調函數:
http.async("/users/{id}") // http://api.demo.com/users/1 .addPathParam("id", 1) .setOnResponse((HttpResult result) -> { // 響應回調 }) .setOnException((Exception e) -> { // 異常回調 }) .setOnComplete((State state) -> { // 完成回調,不管成功失敗都會執行 }) .get();
3.2 HttpResult
HttpResult
是HTTP請求執行完後的結果,它是同步請求方法( get
、post
、put
、delete
)的返回值,也是異步請求響應回調(OnResponse
)的參數,它定義了以下方法:
getState()
獲得請求執行狀態枚舉,它有如下取值:State.CANCELED
請求被取消State.RESPONSED
已收到響應State.TIMEOUT
請求超時State.NETWORK_ERROR
網絡錯誤State.EXCEPTION
其它請求異常
getStatus()
獲得HTTP狀態碼isSuccessful()
是否響應成功,狀態碼在 [200..300) 之間getHeaders()
獲得HTTP響應頭getBody()
獲得響應報文體Body
實例,它定義了以下方法(對同一個Body
實例,如下的toXXX()
類方法只能使用一個且僅能調用一次):toBytes()
返回字節數組toByteStream()
返回字節輸入流toCharStream()
返回字符輸入流toString()
返回字符串toJsonObject()
返回Json對象toJsonArray()
返回Json數組toBean(Class<T> type)
返回根據type自動json解析後的JavaBeantoList(Class<T> type)
返回根據type自動json解析後的JavaBean列表toFile(String filePath)
下載到指定路徑toFile(File file)
下載到指定文件toFolder(String dirPath)
下載到指定目錄toFolder(File dir)
下載到指定目錄getContentType()
返回報文體的媒體類型getContentLength()
返回報文體的字節長度close()
關閉報文體,未對報文體作任何消費時使用,好比只讀取報文頭
getError()
執行中發生的異常,自動捕獲執行請求是發生的 網絡超時、網絡錯誤 和 其它請求異常close()
關閉報文,未對報文體作任何消費時使用,好比只讀取長度
示例,請求結果自動轉Bean和List:
// 自動轉Bean Order order = http.sync("/orders/1") .get().getBody().toBean(Order.class); // 自動轉List List<Order> orders = http.sync("/orders") .get().getBody().toList(Order.class);
示例,下載文件到指定目錄:
String path = "D:/reports/2020-03-01.xlsx"; // 文件保存目錄 // 同步下載 http.sync("/reports/2020-03-01.xlsx") .get().getBody().toFile(path).start(); // 異步下載 http.async("/reports/2020-03-01.xlsx") .setOnResponse((HttpResult result) -> { result.getBody().toFile(path).start(); }) .get();
關於上傳下載更詳細的介紹請看後文:OkHttp 優雅封裝 HttpUtils 之 上傳下載解密。
3.3 HttpCall
HttpCall
對象是異步請求方法(get
、post
、put
、delete
)的返回值,與java
的Future
接口很像,它有以下方法:
cancel()
取消本次請求,返回取消結果isCanceled()
返回請求是否被取消isDone()
返回是否執行完成,包含取消和失敗getResult()
返回執行結果HttpResult
對象,若請求未執行完,則掛起當前線程直到執行完成再返回
取消一個異步請求示例:
HttpCall call = http.async("/users/1").get(); System.out.println(call.isCanceled()); // false boolean success = call.cancel(); // 取消請求 System.out.println(success); // true System.out.println(call.isCanceled()); // true
4 構建HTTP任務
HTTP
對象的sync
與async
方法返回一個HttpTask
對象,該對象提供了可鏈式調用的addXXX
與setXXX
系列方法用於構建任務自己。
-
addHeader(String name, String value)
添加請求頭 -
addHeader(Map<String, String> headers)
添加請求頭 -
addPathParam(String name, Object value)
添加路徑參數:替換URL裏的{name}佔位符 -
addPathParam(Map<String, ?> params)
添加路徑參數:替換URL裏的{name}佔位符 -
addUrlParam(String name, Object value)
添加URL參數:拼接在URL的?以後(查詢參數) -
addUrlParam(Map<String, ?> params)
添加URL參數:拼接在URL的?以後(查詢參數) -
addBodyParam(String name, Object value)
添加Body參數:以表單key=value&的形式放在報文體內(表單參數) -
addBodyParam(Map<String, ?> params)
添加Body參數:以表單key=value&的形式放在報文體內(表單參數) -
addJsonParam(String name, Object value)
添加Json參數:請求體爲Json(支持多層結構) -
addJsonParam(Map<String, ?> params)
添加Json參數:請求體爲Json(支持多層結構) -
setRequestJson(Object json)
設置請求體的Json字符串 或待轉換爲 Json的 JavaBean -
setRequestJson(Object bean, String dateFormat)
設置請求體的Json字符串 或待轉換爲 Json的 JavaBean -
addFileParam(String name, String filePath)
上傳文件 -
addFileParam(String name, File file)
上傳文件 -
addFileParam(String name, String type, InputStream inputStream)
上傳文件 -
addFileParam(String name, String type, String fileName, InputStream input)
上傳文件 -
addFileParam(String name, String type, byte[] content)
上傳文件 -
addFileParam(String name, String type, String fileName, byte[] content)
上傳文件 -
setTag(String tag)
爲HTTP任務添加標籤 -
setRange(long rangeStart)
設置Range頭信息,用於斷點續傳 -
setRange(long rangeStart, long rangeEnd)
設置Range頭信息,用於分塊下載
5 使用標籤
有時候咱們想對HTTP任務加以分類,這時候可使用標籤功能:
http.async("/users") //(1) .setTag("A").get(); http.async("/users") //(2) .setTag("A.B").get(); http.async("/users") //(3) .setTag("B").get(); http.async("/users") //(4) .setTag("B.C").get(); http.async("/users") //(5) .setTag("C").get();
當使用標籤後,就能夠按標籤批量的對HTTP任務進行取消:
int count = http.cancel("B"); //(2)(3)(4)被取消(取消標籤包含"B"的任務) System.out.println(count); // 輸出 3
一樣的,只有異步HTTP任務才能夠被取消。標籤除了能夠用來取消任務,在預處理器中它也能夠發揮做用,請參見下文[6.4 並行預處理器]與[6.5 串行預處理器]。
6 配置 HTTP
6.1 設置 BaseUrl
HTTP http = HTTP.builder() .baseUrl("http://api.demo.com") // 設置 BaseUrl .build();
配置了BaseUrl
以後,具體的請求即可以省略BaseUrl
部分,使得代碼更加簡潔,例如:
http.sync("/users").get() // http://api.demo.com/users http.sync("/auth/signin") // http://api.demo.com/auth/signin .addBodyParam("username", "Jackson") .addBodyParam("password", "xxxxxx") .post() // POST請求
配置了BaseUrl
以後,若有特殊請求,仍然可使用全路徑的方式,一點都不妨礙:
http.sync("https://www.baidu.com").get()
6.2 回調執行器
如何想改變執行回調函數的線程時,能夠配置回調執行器。例如在Android裏,讓全部的回調函數都在UI線程執行,則能夠在構建HTTP
時配置以下:
HTTP http = HTTP.builder() .callbackExecutor((Runnable run) -> { runOnUiThread(run); // 在UI線程執行 }) .build();
該配置影響的回調爲:OnResponse
、OnException
和OnComplete
。
6.3 配置 OkHttpClient
與其餘封裝 OkHttp 的框架不一樣,HttpUtils 並不會遮蔽 OkHttp 自己就很好用的功能,以下:
HTTP http = HTTP.builder() .config((Builder builder) -> { // 配置鏈接池 最小10個鏈接(不配置默認爲 5) builder.connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES)); // 配置鏈接超時時間 builder.connectTimeout(20, TimeUnit.SECONDS); // 配置攔截器 builder.addInterceptor((Chain chain) -> { Request request = chain.request(); // 必須同步返回,攔截器內沒法執行異步操做 return chain.proceed(request); }); // 其它配置: SSL、緩存、代理、事件監聽... }) .build();
6.4 並行預處理器
預處理器(Preprocessor
)可讓咱們在請求發出以前根據業務對請求自己作一些處理,但與 OkHttp 的攔截器(Interceptor
)不一樣:預處理器可讓咱們異步處理這些問題。
例如,當咱們想爲請求任務自動添加Token
頭信息,而Token
只能經過異步方法requestToken
獲取時,這時使用Interceptor
就很難處理了,但可使用預處理器輕鬆解決:
HTTP http = HTTP.builder() .addPreprocessor((PreChain chain) -> { HttpTask<?> task = chain.getTask();// 得到當前的HTTP任務 if (!task.isTagged("Auth")) { // 根據標籤判斷該任務是否須要Token return; } requestToken((String token) -> { // 異步獲取 Token task.addHeader("Token", token);// 爲任務添加頭信息 chain.proceed(); // 繼續當前的任務 }); }) .build();
和Interceptor
同樣,Preprocessor
也能夠添加多個。
6.5 串行預處理器
普通預處理器都是可並行處理的,然而有時咱們但願某個預處理器同時只處理一個任務。好比 當Token
過時時咱們須要去刷新獲取新Token
,而刷新Token
這個操做只能有一個任務去執行,由於若是n
個任務同時執行的話,那麼必有n-1
個任務剛刷新獲得的Token
可能就立馬失效了,而這是咱們所不但願的。
爲了解決這個問題,HttpUtils 提供了串行預處理器,它可讓HTTP任務排好隊,一個一個地進入預處理器:
HTTP http = HTTP.builder() .addSerialPreprocessor((PreChain chain) -> { HttpTask<?> task = chain.getTask(); if (!task.isTagged("Auth")) { return; } // 檢查過時,若須要則刷新Token requestTokenAndRefreshIfExpired((String token) -> { task.addHeader("Token", token); chain.proceed(); // 調用此方法前,不會有其它任務進入該處理器 }); }) .build();
串行預處理器實現了讓HTTP任務排隊串行處理的功能,但值得一提的是:它並無所以而阻塞任何線程!
7 使用 HttpUtils 類
類HttpUtils
本是 1.x 版本里的最重要的核心類,因爲在 2.x 版本里抽象出了HTTP
接口,使得它的重要性已不如往昔。但合理的使用它,仍然能夠帶來很多便利,特別是在沒有IOC容器的環境裏,好比在Android開發和一些工具項目的開發中。
類HttpUtils
共定義了四個靜態方法:
async(String url)
開始一個異步請求 (內容經過一個HTTP
單例實現)sync(String url)
開始一個同步請求 (內容經過一個HTTP
單例實現)cancel(String tag)
按標籤取消請求(內容經過一個HTTP
單例實現)of(HTTP http)
配置HttpUtils
持有的HTTP
實例(不調用此方法前默認使用一個沒有沒有通過任何配置的HTTP
懶實例)
也就是說,能使用http
實例的地方,均可以使用HttpUtils
類,例如:
// 在配置HTTP實例以前,只能使用全路徑方式 List<Role> roles = HttpUtils.sync("http://api.demo.com/roles") .get().getBody().toList(Role.class); // 配置HTTP實例,全局生效 HttpUtils.of(HTTP.builder() .baseUrl("http://api.demo.com") .build()); // 內部使用新的HTTP實例 List<User> users = HttpUtils.sync("/users") .get().getBody().toList(User.class);
下篇文章:OkHttp 優雅封裝 HttpUtils 之 上傳下載解密
曾經在代碼裏放蕩不羈,現在在博文中日夜兼行,只爲今天與你分享成果。若是以爲本文有用,記得關注我,我將帶給你更多。