內部使用了OKIO庫, 此庫中Source表示輸入流(相當於InputStream),Sink表示輸出流(相當於OutputStream) 特點: ·既支持同步請求,也支持非同步請求,同步請求會阻塞當前線程,非同步請求不阻塞當前線程,非同步執行完成後回掉相應的方法 ·支持HTTP/2協議,通過HTTP/2 ...
內部使用了OKIO庫, 此庫中Source表示輸入流(相當於InputStream),Sink表示輸出流(相當於OutputStream)
特點:
·既支持同步請求,也支持非同步請求,同步請求會阻塞當前線程,非同步請求不阻塞當前線程,非同步執行完成後回掉相應的方法
·支持HTTP/2協議,通過HTTP/2 可以讓客戶端中到伺服器的所有請求共用同一個Socket連接
·非HTTP/2 請求時, OKHTTP內部會維護一個線程池,通過線程池可以對HTTP/1.x的連接進行復用,減少延遲
·透明的Gzip處理降低了通信數據的大小
·請求的數據可以進行緩存
重要的類:
·Request 請求類,OKHTTP中大量使用了Builder構建器模式,Request也不例外,其內部有靜態內部類Builder, 封裝了請求url,請求方法method,請求頭headers, 請求體RequestBody,請求標記tag
·Headers類, 被封裝在Request或Response中,其內部使用一維數組String[] namesAndValues表示header的key value信息,可以看到除了導入包中的Util、HttpDate類之外,跟OKHTTP的關聯並不大,可以將此類copy出來單獨分析。在Builder內部類中有一個ArrayList<String> namesAndValues
,當我們添加header鍵值對時,調用add方法,最終調用addLenient()方法將name,value的值依次添加到列表中,列表內部按照name,value,name2,value2,name3,value3...的順序排列,在build()方法中調用toArray方法再轉換為String數組,內部元素的順序不變,這樣在用get方法查找的時候
private static String get(String[] namesAndValues, String name) { for (int i = namesAndValues.length - 2; i >= 0; i -= 2) { if (name.equalsIgnoreCase(namesAndValues[i])) { return namesAndValues[i + 1]; } } return null; }
可以先查找對應的name,得到name對應的索引後,那麼value是緊隨name其後的,自然可以得到value的值。且在查找的時候i的值可以每隔2做一次處理。這樣有些巧妙的設計,避免使用Map去存儲鍵值對,一定程度上提高了效率。 由於在調用get方法時是倒序查找,故添加相同的欄位時,取最後一個的值。如果使用set(String name, String value) 則會移除舊的name和value。需要註意的是有時候響應頭含有多個重覆name的header, 比如有個多 Set-Cookie,這時候可以調用 values(String name)便可以獲得多個值。
·ResponseBody 內部使用了OKIO, 可以得到位元組數組 bytes(), 字元串結果string(),或者是輸入流 byteStream, 特別需要註意的是bytes() string() 方法的註釋,這兩個方法會將響應實體內容全部載入到記憶體中,所以如果響應實體比較大的話,應該考慮使用流的形式讀取。其中charStream()使用響應頭Content-Type指定的字元集來解析響應體。預設是UTF-8
Call
同步方法execute() 會阻塞當前線程直到有響應。
非同步方法 enqueue(Callback responseCallback)
OKHttpClient
內部也使用Builder構建器模式,可以配置超時時間,緩存,代理,攔截器等
Force a Network Response
在某些情形下,例如點擊了刷新按鈕之後,有必要跳過緩存,直接向伺服器獲取資源,為了獲得刷新後的資源可以 添加 Connection.addRequestProperty(“Cache-Control”, “no-cache”), 或者可以直接使用 connection.addRequestProperty(“Cache-Control”, “max-age=0”);
Force a Cache Response
Connection.addRequestProperty(“Cache-Control”, “only-if-cached”);
請求的流程:
·非同步請求
創建Request ,執行client.newCall(request).enqueue(Callback)方法,跟蹤newCall方法,方法內部new了一個RealCall,RealCall實現了Call介面,查看RealCall的enqueue方法,最終調用client.dispatcher().enqueue(new AsyncCall(responseCallback);其中Dispatcher內部封裝了線程池(可以看到預設的線程池也是沒有核心線程, 和Android-Async-http的預設線程池一致),查看Dispatcher的enqueue方法可以看到如果正在請求的call數量小於最大請求數,則將call添加到runningAsyncCalls中,並提交call到線程池中(AsyncCall實現了Runnable的介面), 查看AsyncCall的構造方法,可以看到傳入響應的回調,AsyncCall提交到線程池中後,run方法中會調用execute方法,
在AsyncCall的execute方法中通過getResponseWithInterceptorChain()方法得到Response,併進行響應的請求成功或失敗的回調,然後調用finished方法移除之前添加到Queue中的call。
上傳文件:
OkHttpClient httpClient = new OkHttpClient(); RequestBody requestBody = FormBody.create(MediaType.parse("image/jpeg"), new File(getExternalCacheDir()+ File.separator +"IMG_20171106_202814.jpg")); MultipartBody multipartBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("uploadimg", "IMG_20171106_202814.jpg", requestBody) .build(); Request request = new Request.Builder().url("http://www.chuantu.biz/upload.php") .addHeader("Cache-Control", "max-age=0") .addHeader("Upgrade-Insecure-Requests", "1") .addHeader("Accept-Language","zh-CN,zh;q=0.8") .post(multipartBody).build(); Call call = httpClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { Log.d("song", response.body().string()); } });
Fidddler抓包的截圖