日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

OkHttp用法详解

發布時間:2023/12/29 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OkHttp用法详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

OkHttp的基本使用

1、首先在工程的app模塊下添加okhttp的依賴

implementation 'com.squareup.okhttp3:okhttp:3.12.0'

同步GET請求

同步GET的意思是一直等待http請求, 直到返回了響應. 在這之間會阻塞進程, 所以通過get不能在Android的主線程中執行, 否則會報錯.

private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt").build();Response response = client.newCall(request).execute();if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);Headers responseHeaders = response.headers();for (int i = 0; i < responseHeaders.size(); i++) {System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));}System.out.println(response.body().string()); }

OkHttpClient實現了Call.Factory接口, 是Call的工廠類, Call負責發送執行請求和讀取響應.
Request代表Http請求, 通過Request.Builder輔助類來構建.client.newCall(request)通過傳入一個http request, 返回一個Call調用. 然后執行execute()方法, 同步獲得
Response代表Http請求的響應. response.body()是ResponseBody類, 代表響應體, 可以通過responseBody.string()獲得字符串的表達形式, 或responseBody.bytes()獲得字節數組的表達形式, 這兩種形式都會把文檔加入到內存. 也可以通過responseBody.charStream()和responseBody.byteStream()返回流來處理.

上述代碼完成的功能是下載一個文件, 打印他的響應頭, 以string形式打印響應體.
響應體的string()方法對于小文檔來說十分方便高效. 但是如果響應體太大(超過1MB), 應避免使用 string()方法, 因為它會將把整個文檔加載到內存中.
對于超過1MB的響應body, 應使用流的方式來處理響應body. 這和我們處理xml文檔的邏輯是一致的, 小文件可以載入內存樹狀解析, 大文件就必須流式解析.

異步GET

異步GET是指在另外的工作線程中執行http請求, 請求時不會阻塞當前的線程, 所以可以在Android主線程中使用.
下面是在一個工作線程中下載文件, 當響應可讀時回調Callback接口. 當響應頭準備好后, 就會調用Callback接口, 所以讀取響應體時可能會阻塞. OkHttp現階段不提供異步api來接收響應體。

//1.拿到okHttpClient對象,可以設置連接超時等OkHttpClient okHttpClient=new OkHttpClient();//2.構造Request請求對象,可以增加頭addHeader等Request.Builder builder = new Request.Builder();//url()中可以放入網址Request request = builder.get().url("http://publicobject.com/helloworld.txt").build();//3.將Request封裝為CallCall call = okHttpClient.newCall(request);//4.執行call//方法一Response response=call.execute();//匯拋出IO異常,同步方法//方法二,異步方法,放到隊列中,處于子線程中,無法更新UIcall.enqueue(new Callback() {//請求時失敗時調用@Overridepublic void onFailure(Call call, IOException e) {}//請求成功時調用@Overridepublic void onResponse(Call call, Response response) throws IOException {//處于子線程中,能夠進行大文件下載,但是無法更新UIfinal String res = response.body().string();//請求成功時返回的東西//InputStream is=response.body().byteStream();// 執行IO操作時,能夠下載很大的文件,并且不會占用很大內存/*** runOnUiThread方法切換到主線程中,或者用handler機制也可以*/runOnUiThread(new Runnable() {@Overridepublic void run() {// 更新ui}});}});

Post提交String

下面是使用HTTP POST提交請求到服務. 這個例子提交了一個json字符串到web服務. 因為整個請求體都在內存中, 因此避免使用此api提交大文檔(大于1MB)

OkHttpClient okHttpClient=new OkHttpClient();RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain;charset=utf-8"), "{username:hyman,password:123}");Request.Builder builder = new Request.Builder();Request request = builder.url(mBaseUrl + "postString").post(requestBody).build();Call call = okHttpClient.newCall(request);//4.執行call//方法一Response response=call.execute();//匯拋出IO異常,同步方法//方法二,異步方法,放到隊列中,處于子線程中,無法更新UIcall.enqueue(new Callback() {//請求時失敗時調用@Overridepublic void onFailure(Call call, IOException e) {}//請求成功時調用@Overridepublic void onResponse(Call call, Response response) throws IOException {//處于子線程中,能夠進行大文件下載,但是無法更新UIfinal String res = response.body().string();//請求成功時返回的東西//InputStream is=response.body().byteStream();// 執行IO操作時,能夠下載很大的文件,并且不會占用很大內存/*** runOnUiThread方法切換到主線程中,或者用handler機制也可以*/runOnUiThread(new Runnable() {@Overridepublic void run() {// 更新ui}});}});

Post提交流

以流的方式POST提交請求體. 請求體的內容由流寫入產生. 這個例子是流直接寫入Okio的BufferedSink. 你的程序可能會使用OutputStream, 你可以使用BufferedSink.outputStream()來獲取. OkHttp的底層對流和字節的操作都是基于Okio庫, Okio庫也是Square開發的另一個IO庫, 填補I/O和NIO的空缺, 目的是提供簡單便于使用的接口來操作IO.

public static final MediaType MEDIA_TYPE_MARKDOWN= MediaType.parse("text/x-markdown; charset=utf-8");private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {RequestBody requestBody = new RequestBody() {@Override public MediaType contentType() {return MEDIA_TYPE_MARKDOWN;}@Override public void writeTo(BufferedSink sink) throws IOException {sink.writeUtf8("Numbers\n");sink.writeUtf8("-------\n");for (int i = 2; i <= 997; i++) {sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));}}private String factor(int n) {for (int i = 2; i < n; i++) {int x = n / i;if (x * i == n) return factor(x) + " × " + i;}return Integer.toString(n);}};Request request = new Request.Builder().url("https://api.github.com/markdown/raw").post(requestBody).build();Response response = client.newCall(request).execute();if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println(response.body().string()); }

Post提交文件

File file = new File(Environment.getExternalStorageDirectory(), "banner.png");if (!file.exists()) {return;}//1.拿到okHttpClient對象OkHttpClient okHttpClient = new OkHttpClient();//mime typeRequestBody requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);Request.Builder builder = new Request.Builder();Request request = builder.url(mBaseUrl + "postFile").post(requestBody).build();Call call = okHttpClient.newCall(request);//4.執行call//方法一Response response=call.execute();//匯拋出IO異常,同步方法//方法二,異步方法,放到隊列中,處于子線程中,無法更新UIcall.enqueue(new Callback() {//請求時失敗時調用@Overridepublic void onFailure(Call call, IOException e) {Log.d("報錯了嗎", "onFailure: 報了");e.printStackTrace();}//請求成功時調用@Overridepublic void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {//處于子線程中,能夠進行大文件下載,但是無法更新UILog.d("執行成功", "onResponse: 成功");final String res = response.body().string();//請求成功時返回的東西//InputStream is=response.body().byteStream();// 執行IO操作時,能夠下載很大的文件,并且不會占用很大內存/*** runOnUiThread方法切換到主線程中,或者用handler機制也可以*/runOnUiThread(new Runnable() {@Overridepublic void run() {// 更新ui}});}});

Post提交表單

使用FormEncodingBuilder來構建和HTML標簽相同效果的請求體. 鍵值對將使用一種HTML兼容形式的URL編碼來進行編碼.

//1.拿到okHttpClient對象OkHttpClient okHttpClient = new OkHttpClient();//2.構造Request請求對象//構造requestBody,get請求不需要FormBody.Builder requestBodyBuilder = new FormBody.Builder();//提交到服務器端的請求體RequestBody requestBody = requestBodyBuilder.add("username", "AI").add("password", "12345678").build();Request.Builder builder = new Request.Builder();Request request = builder.url(mBaseUrl + "login").post(requestBody).build();Call call = okHttpClient.newCall(request);//4.執行call//方法一Response response=call.execute();//匯拋出IO異常,同步方法//方法二,異步方法,放到隊列中,處于子線程中,無法更新UIcall.enqueue(new Callback() {//請求時失敗時調用@Overridepublic void onFailure(Call call, IOException e) {}//請求成功時調用@Overridepublic void onResponse(Call call, Response response) throws IOException {//處于子線程中,能夠進行大文件下載,但是無法更新UIfinal String res = response.body().string();//請求成功時返回的東西//InputStream is=response.body().byteStream();// 執行IO操作時,能夠下載很大的文件,并且不會占用很大內存/*** runOnUiThread方法切換到主線程中,或者用handler機制也可以*/runOnUiThread(new Runnable() {@Overridepublic void run() {// 更新ui}});}});

Post提交分塊請求

MultipartBody.Builder可以構建復雜的請求體, 與HTML文件上傳形式兼容. 多塊請求體中每塊請求都是一個請求體, 可以定義自己的請求頭. 這些請求頭可以用來描述這塊請求, 例如它的Content-Disposition. 如果Content-Length和Content-Type可用的話, 他們會被自動添加到請求頭中.

File file = new File(Environment.getExternalStorageDirectory(), "uz_splash_bg.png");if (!file.exists()) {return;}OkHttpClient okHttpClient=new OkHttpClient();//mime typeMultipartBody.Builder multipartBuilder = new MultipartBody.Builder();RequestBody requestBody = multipartBuilder.setType(MultipartBody.FORM).addPart(Headers.of("Content-Disposition", "form-data; name=\"" + "username" + "\""),RequestBody.create(null, "hyman")).addPart(Headers.of("Content-Disposition", "form-data; name=\"" + "password" + "\""),RequestBody.create(null, "123")).addFormDataPart("mPhoto", "hyman.png", RequestBody.create(MediaType.parse("application/octet-stream"), file)).build();//mPhoto是File的一個域,value//顯示上傳進度,把post的中的requestBody改為countingRequestBody就行了/**CountingRequestBody countingRequestBody=new CountingRequestBody(requestBody, new CountingRequestBody.Listener() {@Override public void onRequestProgress(long byteWritten, long contentLength) {L.e(byteWritten+"/"+contentLength);}});*/Request.Builder builder = new Request.Builder();Request request = builder.url(mBaseUrl + "uploadInfo").post(requestBody).build();Call call = okHttpClient.newCall(request);//4.執行call//方法一Response response=call.execute();//匯拋出IO異常,同步方法//方法二,異步方法,放到隊列中,處于子線程中,無法更新UIcall.enqueue(new Callback() {//請求時失敗時調用@Overridepublic void onFailure(Call call, IOException e) {}//請求成功時調用@Overridepublic void onResponse(Call call, Response response) throws IOException {//處于子線程中,能夠進行大文件下載,但是無法更新UIfinal String res = response.body().string();//請求成功時返回的東西//InputStream is=response.body().byteStream();// 執行IO操作時,能夠下載很大的文件,并且不會占用很大內存/*** runOnUiThread方法切換到主線程中,或者用handler機制也可以*/runOnUiThread(new Runnable() {@Overridepublic void run() {// 更新ui}});}});

設置響應頭和提取響應頭

典型的HTTP頭像是一個Map<String, String> : 每個字段都有一個或沒有值. 但是一些頭允許多個值, 像Guava的Multimap.
例如: HTTP響應里面提供的Vary響應頭, 就是多值的. OkHttp的api試圖讓這些情況都適用.
當寫請求頭的時候, 使用header(name, value)可以設置唯一的name、value. 如果已經有值, 舊的將被移除, 然后添加新的. 使用addHeader(name, value)可以添加多值(添加, 不移除已有的).
當讀取響應頭時, 使用header(name)返回最后出現的name、value. 通常情況這也是唯一的name、value. 如果沒有值, 那么header(name)將返回null. 如果想讀取字段對應的所有值, 使用headers(name)會返回一個list.
為了獲取所有的Header, Headers類支持按index訪問.

private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {// 請求頭的設置// header()會覆蓋之前已設置的同名請求頭,而addHeader()不會Request request = new Request.Builder().url("https://api.github.com/repos/square/okhttp/issues").header("User-Agent", "OkHttp Headers.java").addHeader("Accept", "application/json; q=0.5").addHeader("Accept", "application/vnd.github.v3+json").build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);// response.header(headerName)獲取的是響應頭對應字段的最新值System.out.println("Server: " + response.header("Server"));System.out.println("Date: " + response.header("Date"));// 獲取響應頭集合System.out.println("Vary: " + response.headers("Vary"));}}

使用Gson來解析JSON響應

Gson是一個在JSON和Java對象之間轉換非常方便的api庫. 這里我們用Gson來解析Github API的JSON響應.
注意: ResponseBody.charStream()使用響應頭Content-Type指定的字符集來解析響應體. 默認是UTF-8.

private final OkHttpClient client = new OkHttpClient();private final Gson gson = new Gson();public void run() throws Exception {Request request = new Request.Builder().url("https://api.github.com/gists/c2a7c39532239ff261be").build();Response response = client.newCall(request).execute();if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);Gist gist = gson.fromJson(response.body().charStream(), Gist.class);for (Map.Entry<String, GistFile> entry : gist.files.entrySet()) {System.out.println(entry.getKey());System.out.println(entry.getValue().content);}}static class Gist {Map<String, GistFile> files;}static class GistFile {String content;}

響應緩存

為了緩存響應, 你需要一個你可以讀寫的緩存目錄, 和緩存大小的限制. 這個緩存目錄應該是私有的, 不信任的程序應不能讀取緩存內容.
一個緩存目錄同時擁有多個緩存訪問是錯誤的. 大多數程序只需要調用一次new OkHttp(), 在第一次調用時配置好緩存, 然后其他地方只需要調用這個實例就可以了. 否則兩個緩存示例互相干擾, 破壞響應緩存, 而且有可能會導致程序崩潰.
響應緩存使用HTTP頭作為配置. 你可以在請求頭中添加Cache-Control: max-stale=3600 , OkHttp緩存會支持. 你的服務通過響應頭確定響應緩存多長時間, 例如使用Cache-Control: max-age=9600.

private final OkHttpClient client;public CacheResponse(File cacheDirectory) throws Exception {int cacheSize = 10 * 1024 * 1024; // 10 MiBCache cache = new Cache(cacheDirectory, cacheSize);client = new OkHttpClient();client.setCache(cache); }public void run() throws Exception {Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt").build();Response response1 = client.newCall(request).execute();if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);String response1Body = response1.body().string();System.out.println("Response 1 response: " + response1);System.out.println("Response 1 cache response: " + response1.cacheResponse());System.out.println("Response 1 network response: " + response1.networkResponse());Response response2 = client.newCall(request).execute();if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);String response2Body = response2.body().string();System.out.println("Response 2 response: " + response2);System.out.println("Response 2 cache response: " + response2.cacheResponse());System.out.println("Response 2 network response: " + response2.networkResponse());System.out.println("Response 2 equals Response 1? " + response1Body.equals(response2Body)); }

如果需要阻值response使用緩存, 使用CacheControl.FORCE_NETWORK. 如果需要阻值response使用網絡, 使用CacheControl.FORCE_CACHE.
警告: 如果你使用FORCE_CACHE, 但是response要求使用網絡, OkHttp將會返回一個504 Unsatisfiable Request響應.

Force a Network Response

有些時候, 比如用戶剛剛點擊刷新按鈕, 這時必須跳過緩存, 直接從服務器抓取數據. 為了強制全面刷新, 我們需要添加no-cache指令:

connection.addRequestProperty("Cache-Control", "no-cache");

這樣就可以強制每次請求直接發送給源服務器, 而不經過本地緩存版本的校驗, 常用于需要確認認證的應用和嚴格要求使用最新數據的應用.

Force a Cache Response

有時你會想立即顯示資源. 這樣即使在后臺正下載著最新資源, 你的客戶端仍然可以先顯示原有資源, 畢竟有個東西顯示比沒有東西顯示要好.
如果需要限制讓請求優先使用本地緩存資源, 需要增加only-if-cached指令:

try {connection.addRequestProperty("Cache-Control", "only-if-cached");InputStream cached = connection.getInputStream();// the resource was cached! show itcatch (FileNotFoundException e) {// the resource was not cached} }

取消一個Call

使用Call.cancel()可以立即停止掉一個正在執行的call. 如果一個線程正在寫請求或者讀響應, 將會引發IOException. 當call沒有必要的時候, 使用這個api可以節約網絡資源. 例如當用戶離開一個應用時, 不管同步還是異步的call都可以取消.
你可以通過tags來同時取消多個請求. 當你構建一請求時, 使用RequestBuilder.tag(tag)來分配一個標簽, 之后你就可以用OkHttpClient.cancel(tag)來取消所有帶有這個tag的call.

private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {Request request = new Request.Builder().url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay..build();final long startNanos = System.nanoTime();final Call call = client.newCall(request);// Schedule a job to cancel the call in 1 second.executor.schedule(new Runnable() {@Override public void run() {System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f);call.cancel();System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f);}}, 1, TimeUnit.SECONDS);try {System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f);Response response = call.execute();System.out.printf("%.2f Call was expected to fail, but completed: %s%n",(System.nanoTime() - startNanos) / 1e9f, response);} catch (IOException e) {System.out.printf("%.2f Call failed as expected: %s%n",(System.nanoTime() - startNanos) / 1e9f, e);}}

超時

沒有響應時使用超時結束call. 沒有響應的原因可能是客戶點鏈接問題、服務器可用性問題或者這之間的其他東西. OkHttp支持連接超時, 讀取超時和寫入超時.

private final OkHttpClient client;public ConfigureTimeouts() throws Exception {client = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).build();}public void run() throws Exception {Request request = new Request.Builder().url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay..build();Response response = client.newCall(request).execute();System.out.println("Response completed: " + response);}

每個call的配置

使用OkHttpClient, 所有的HTTP Client配置包括代理設置、超時設置、緩存設置. 當你需要為單個call改變配置的時候, 調用OkHttpClient.newBuilder(). 這個api將會返回一個builder, 這個builder和原始的client共享相同的連接池, 分發器和配置.
下面的例子中,我們讓一個請求是500ms的超時、另一個是3000ms的超時。
每一個Call(其實現是RealCall)只能執行一次,否則會報異常,具體參見 RealCall#execute()

private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {Request request = new Request.Builder().url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay..build();try {// Copy to customize OkHttp for this request.OkHttpClient copy = client.newBuilder().readTimeout(500, TimeUnit.MILLISECONDS).build();Response response = copy.newCall(request).execute();System.out.println("Response 1 succeeded: " + response);} catch (IOException e) {System.out.println("Response 1 failed: " + e);}try {// Copy to customize OkHttp for this request.OkHttpClient copy = client.newBuilder().readTimeout(3000, TimeUnit.MILLISECONDS).build();Response response = copy.newCall(request).execute();System.out.println("Response 2 succeeded: " + response);} catch (IOException e) {System.out.println("Response 2 failed: " + e);}}

處理驗證

這部分和HTTP AUTH有關.

HTTP AUTH

使用HTTP AUTH需要在server端配置http auth信息, 其過程如下:

  • 客戶端發送http請求
  • 服務器發現配置了http auth, 于是檢查request里面有沒有"Authorization"的http header
  • 如果有, 則判斷Authorization里面的內容是否在用戶列表里面, Authorization header的典型數據為"Authorization: Basic jdhaHY0=", 其中Basic表示基礎認證, jdhaHY0=是base64編碼的"user:passwd"字符串. 如果沒有,或者用戶密碼不對,則返回http code 401頁面給客戶端.
  • 標準的http瀏覽器在收到401頁面之后, 應該彈出一個對話框讓用戶輸入帳號密碼; 并在用戶點確認的時候再次發出請求, 這次請求里面將帶上Authorization header.
  • 一次典型的訪問場景是:

  • 瀏覽器發送http請求(沒有Authorization header)
  • 服務器端返回401頁面
  • 瀏覽器彈出認證對話框
  • 用戶輸入帳號密碼,并點確認
  • 瀏覽器再次發出http請求(帶著Authorization header)
  • 服務器端認證通過,并返回頁面
  • 瀏覽器顯示頁面
  • OkHttp認證

    OkHttp會自動重試未驗證的請求. 當響應是401 Not Authorized時,Authenticator會被要求提供證書. Authenticator的實現中需要建立一個新的包含證書的請求. 如果沒有證書可用, 返回null來跳過嘗試.
    使用Response.challenges()來獲得任何authentication challenges的 schemes 和 realms. 當完成一個Basic challenge, 使用Credentials.basic(username, password)來解碼請求頭.

    private final OkHttpClient client;public Authenticate() {client = new OkHttpClient.Builder().authenticator(new Authenticator() {@Override public Request authenticate(Route route, Response response) throws IOException {System.out.println("Authenticating for response: " + response);System.out.println("Challenges: " + response.challenges());String credential = Credentials.basic("jesse", "password1");return response.request().newBuilder().header("Authorization", credential).build();}}).build();}public void run() throws Exception {Request request = new Request.Builder().url("http://publicobject.com/secrets/hellosecret.txt").build();Response response = client.newCall(request).execute();if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println(response.body().string());}

    當認證無法工作時, 為了避免多次重試, 你可以返回空來放棄認證. 例如, 當exact credentials已經嘗試過, 你可能會直接想跳過認證, 可以這樣做:

    if (credential.equals(response.request().header("Authorization"))) {return null; // If we already failed with these credentials, don't retry.}

    當重試次數超過定義的次數, 你若想跳過認證, 可以這樣做:

    if (responseCount(response) >= 3) {return null; // If we've failed 3 times, give up.}private int responseCount(Response response) {int result = 1;while ((response = response.priorResponse()) != null) {result++;}return result;}

    攔截器-interceptor

    OkHttp的攔截器鏈可謂是其整個框架的精髓,用戶可傳入的 interceptor 分為兩類:

    • ①一類是全局的 interceptor,該類 interceptor 在整個攔截器鏈中最早被調用,通過OkHttpClient.Builder#addInterceptor(Interceptor) 傳入;
    • ②另外一類是非網頁請求的 interceptor,這類攔截器只會在非網頁請求中被調用,并且是在組裝完請求之后,真正發起網絡請求前被調用,所有的 interceptor 被保存在List interceptors 集合中,按照添加順序來逐個調用,具體可參考RealCall#getResponseWithInterceptorChain() 方法。通過
      OkHttpClient.Builder#addNetworkInterceptor(Interceptor) 傳入;

    這里舉一個簡單的例子,例如有這樣一個需求,我要監控App通過 OkHttp 發出的所有原始請求,以及整個請求所耗費的時間,針對這樣的需求就可以使用第一類全局的 interceptor 在攔截器鏈頭去做。

    OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor()).build(); Request request = new Request.Builder().url("http://www.publicobject.com/helloworld.txt").header("User-Agent", "OkHttp Example").build(); okHttpClient.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {Log.d(TAG, "onFailure: " + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {ResponseBody body = response.body();if (body != null) {Log.d(TAG, "onResponse: " + response.body().string());body.close();}} }); public class LoggingInterceptor implements Interceptor {private static final String TAG = "LoggingInterceptor";@Overridepublic Response intercept(Chain chain) throws IOException {Request request = chain.request();long startTime = System.nanoTime();Log.d(TAG, String.format("Sending request %s on %s%n%s",request.url(), chain.connection(), request.headers()));Response response = chain.proceed(request);long endTime = System.nanoTime();Log.d(TAG, String.format("Received response for %s in %.1fms%n%s",response.request().url(), (endTime - startTime) / 1e6d, response.headers()));return response;} }

    針對這個請求,打印出來的結果

    Sending request http://www.publicobject.com/helloworld.txt on null User-Agent: OkHttp ExampleReceived response for https://publicobject.com/helloworld.txt in 1265.9ms Server: nginx/1.10.0 (Ubuntu) Date: Wed, 28 Mar 2018 08:19:48 GMT Content-Type: text/plain Content-Length: 1759 Last-Modified: Tue, 27 May 2014 02:35:47 GMT Connection: keep-alive ETag: "5383fa03-6df" Accept-Ranges: bytes

    注意到一點是這個請求做了重定向,原始的 request url 是 http://www.publicobject.com/helloworld.tx,而響應的 request url 是 https://publicobject.com/helloworld.txt,這說明一定發生了重定向,但是做了幾次重定向其實我們這里是不知道的,要知道這些的話,可以使用 addNetworkInterceptor()去做。更多的關于 interceptor的使用以及它們各自的優缺點,請移步OkHttp Interceptors 攔截器

    自定義dns服務

    Okhttp默認情況下使用的是系統

    其他

    推薦讓 OkHttpClient 保持單例,用同一個 OkHttpClient 實例來執行你的所有請求,因為每一個 OkHttpClient 實例都擁有自己的連接池和線程池,重用這些資源可以減少延時和節省資源,如果為每個請求創建一個 OkHttpClient 實例,顯然就是一種資源的浪費。當然,也可以使用如下的方式來創建一個新的 OkHttpClient 實例,它們共享連接池、線程池和配置信息。

    OkHttpClient eagerClient = client.newBuilder().readTimeout(500, TimeUnit.MILLISECONDS).build();Response response = eagerClient.newCall(request).execute();

    資源釋放

    OkHttp中被掛起的線程和連接都將會在保持空閑時自動回收。如果我們想要主動釋放資源,可以使用如下方式,之后所有的Call對象執行請求都將被拒絕。

    client.dispatcher().executorService().shutdown();

    清理連接池可以使用如下方式(連接池的守護線程可能不會立即退出):

    client.connectionPool().evictAll();

    如果希望關閉緩存,可以使用如下方式:

    client.cache().close();

    補充一點

    MediaType媒體類型:決定瀏覽器將以什么形式、什么編碼對資源進行解析

    Content-Type:也屬于MediaType媒體類型,主要用于在請求頭中指定資源的MediaType
    一、MediaType類型
    類型 描述
    text/html HTML格式
    text/plain 純文本格式,空格轉換為 “+” 加號,但不對特殊字符編碼
    text/xml XML格式
    text/x-markdown Markdown格式
    image/gif gif圖片格式
    image/jpeg jpg圖片格式
    image/png png圖片格式
    application/xhtml+xml XHTML格式
    application/xml XML數據格式
    application/json 用來告訴服務端,消息主體是序列化后的JSON字符串
    application/pdf pdf格式
    application/msword Word文檔格式
    application/octet-stream 二進制流數據(如常見的文件下載)
    application/x-www-form-urlencoded 參數為鍵值對形式,在發送前編碼所有字符(默認)。瀏覽器的原生 <form encType=”” 表單提交類型,如果不設置 enctype 屬性,那么最終就會以 application/x-www-form-urlencoded 方式提交數據
    multipart/form-data 不對字符編碼,發送大量二進制數據或包含non-ASCII字符的文本,application/x-www-form-urlencoded是效率低下的(需要用更多字符表示一個non-ASCII字符)。需要設定“ <form enctype=‘multipart/form-data’”

    二、MediaType對象解析

    MediaType對象包含了三種信息:type 、subtype、charset,一般將這些信息傳入parse()方法中,這樣就可以解析出MediaType對象

    例子1:

    text/x-markdown; charset=utf-8

    type值是text,表示是文本這一大類;
    / 后面的x-markdown是subtype,表示是文本這一大類下的markdown這一小類;
    charset=utf-8 則表示采用UTF-8編碼

    參考文章
    Android OkHttp完全解析 是時候來了解OkHttp了
    OkHttp使用教程
    okhttp3使用總結
    Okhttp3基本使用
    OkHttp使用完全教程

    總結

    以上是生活随笔為你收集整理的OkHttp用法详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。