OkHttp使用小記——8分鐘進階

轉(zhuǎn)載注明出處:簡書-十個雨點

上一篇中,我們學習了如何使用OkHttp進行Get和Post操作软啼,本篇將教你如何對網(wǎng)絡請求和應答進行更多定制。

Call call = new OkHttpClient().newCall(new Request.Builder().build());
Response response=call.execute();

上面一行代碼中展示了使用OkHttp發(fā)送一次請求涉及的類溉委,我們來一一解析点待。

OkHttpClient

OkHttpClient是OkHttp的核心功能的執(zhí)行者,可以通過

OkHttpClient client=new OkHttpClient();

來創(chuàng)建默認的OkHttpClient對象畴椰,也可以使用:

OkHttpClient client=new OkHttpClient.Builder()
        .connectTimeout(60, TimeUnit.SECONDS)       //設置連接超時
        .readTimeout(60, TimeUnit.SECONDS)          //設置讀超時
        .writeTimeout(60,TimeUnit.SECONDS)          //設置寫超時
        .retryOnConnectionFailure(true)             //是否自動重連
        .build();                                   //構(gòu)建OkHttpClient對象

來構(gòu)建自定義的OkHttpClient 對象臊诊,上面代碼只給出了常用的設置項,其他設置項比較復雜斜脂,我們下篇再說抓艳。

使用OkHttpClient 的時候需要注意以下幾點:

  1. 最好只使用一個共享的OkHttpClient 實例,將所有的網(wǎng)絡請求都通過這個實例處理帚戳。因為每個OkHttpClient 實例都有自己的連接池和線程池玷或,重用這個實例能降低延時,減少內(nèi)存消耗片任,而重復創(chuàng)建新實例則會浪費資源偏友。
  2. OkHttpClient的線程池和連接池在空閑的時候會自動釋放,所以一般情況下不需要手動關閉对供,但是如果出現(xiàn)極端內(nèi)存不足的情況位他,可以使用以下代碼釋放內(nèi)存:
client.dispatcher().executorService().shutdown();   //清除并關閉線程池
client.connectionPool().evictAll();                 //清除并關閉連接池
client.cache().close();                             //清除cache
  1. 如果對一些請求需要特殊定制,可以使用
OkHttpClient eagerClient = client.newBuilder() 
       .readTimeout(500, TimeUnit.MILLISECONDS)
       .build();

這樣創(chuàng)建的實例與原實例共享線程池产场、連接池和其他設置項鹅髓,只需進行少量配置就可以實現(xiàn)特殊需求。

Request

Request是網(wǎng)絡請求的對象涝动,其本身的構(gòu)造函數(shù)是private的迈勋,只能通過Request.Builder來構(gòu)建。下面代碼展示了常用的設置項醋粟。

Request request = new Request.Builder()
        .url("https://api.github.com/repos/square/okhttp/issues")                          //設置訪問url
        .get()                                                                             //類似的有post靡菇、delete、patch米愿、head厦凤、put等方法,對應不同的網(wǎng)絡請求方法
        .header("User-Agent", "OkHttp Headers.java")                                       //設置header
        .addHeader("Accept", "application/json; q=0.5")                                    //添加header
        .removeHeader("User-Agent")                                                        //移除header
        .headers(new Headers.Builder().add("User-Agent", "OkHttp Headers.java").build())   //移除原有所有header育苟,并設置新header
        .addHeader("Accept", "application/vnd.github.v3+json")
        .build();                                                                          //構(gòu)建request

RequestBody

在Request中使用post较鼓、patch等方法時,需要傳入一個RequestBody參數(shù),除了上一節(jié)講到的構(gòu)造方法外博烂,RequestBody還有兩個子類:FormBody和MultipartBody香椎。

FromBody用于提交表單鍵值對,其作用類似于HTML中的<form>標記禽篱。

RequestBody formBody = new FormBody.Builder()                             //提交表單鍵值對
        .add("platform", "android")                                       //添加鍵值對
        .add("name", "XXX")
        .add("subject", "Hello")
        .addEncoded(URLEncoder.encode("詳細","GBK"),                       //添加已編碼的鍵值對
                    URLEncoder.encode("無","GBK"))
        .add("描述","你好")                                                //其實會自動編碼畜伐,但是無法控制編碼格式
        .build();

使用MultipartBody.Builder可以構(gòu)建與HTML文件上傳格式兼容的復雜請求體。多塊請求體中每塊請求都是一個獨立的請求體躺率,都可以定義自己的請求頭玛界。這些請求頭應該用于描述對應的請求體,例如Content-Disposition悼吱,Content-Length慎框,和Content-Type會自動被添加到請求頭中。

RequestBody requestBody = new MultipartBody.Builder()
        .setType(MultipartBody.FORM)
        .addPart(
                Headers.of("Content-Disposition", "form-data; name=\"title\""),
                RequestBody.create(null, "Logo"))
        .addPart(
                Headers.of("Content-Disposition", "form-data; name=\"image\""),
                RequestBody.create(MediaType.parse("image/png"), new File("website/static/logo.png")))
        .addFormDataPart("discription","beautiful")
        .build();

了上面這些現(xiàn)成的類和方法以外后添,還可以用繼承RequestBody的方式自定義實現(xiàn)笨枯,比如下例所示是以流的方式POST提交RequestBody,其中BufferedSink是Okio的API遇西,可以使用BufferedSink.outputStream()來得到OutputStream猎醇。

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());
}

Call

Call對象表示一個已經(jīng)準備好可以執(zhí)行的請求,用這個對象可以查詢請求的執(zhí)行狀態(tài)努溃,或者取消當前請求。它具有以下方法:

Call call=client.newCall(request);              //獲取Call對象
Response response=call.execute();               //同步執(zhí)行網(wǎng)絡請求阻问,不要在主線程執(zhí)行
call.enqueue(new Callback());                   //異步執(zhí)行網(wǎng)絡請求
call.cancel();                                  //取消請求
call.isCanceled();                              //查詢是否取消
call.isExecuted();                              //查詢是否被執(zhí)行過

要注意的是梧税,每個Call對象只能執(zhí)行一次請求。如果想重復執(zhí)行相同的請求称近,可以:

Call reCall=client.newCall(call.request());     //獲取另一個相同配置的Call對象

Response

Response是網(wǎng)絡請求的結(jié)果下面是一些常用方法:

Response response=call.execute();               //獲取Response對象
response.code();                                //請求的狀態(tài)碼
response.isSuccessful();                        //如果狀態(tài)碼為[200..300)第队,則表明請求成功
Headers headers=response.headers();              //獲取響應頭
List<String> names=response.headers("name");     //獲取響應頭中的某個字段
ResponseBody body=response.body();             //獲取響應體

其中ResponseBody代表響應體,用于操作網(wǎng)絡請求返回的內(nèi)容刨秆。常用方法如下:

body.contentLength();                           //body的長度
String content=body.string();                   //以字符串形式解碼body
byte[] byteContent=body.bytes();                //以字節(jié)數(shù)組形式解碼body
InputStreamReader reader=body.charStream();     //將body以字符流的形式解碼
InputStream inputStream=body.byteStream();      //將body以字節(jié)流的形式解碼

ResponseBody還有一些注意事項

  1. ResponseBody必須關閉凳谦,不然可能造成資源泄漏,你可以通過以下方法關閉ResponseBody,對同一個ResponseBody只要關閉一次就可以了衡未。
Response.close();
Response.body().close();
Response.body().source().close();
Response.body().charStream().close();
Response.body().byteString().close();
Response.body().bytes();
Response.body().string();
  1. ResponseBody只能被消費一次尸执,也就是string(),bytes(),byteStream()或 charStream()方法只能調(diào)用其中一個。

  2. 如果ResponseBody中的數(shù)據(jù)很大缓醋,則不應該使用bytes() 或 string()方法如失,它們會將結(jié)果一次性讀入內(nèi)存,而應該使用byteStream()或 charStream()送粱,以流的方式讀取數(shù)據(jù)褪贵。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子脆丁,更是在濱河造成了極大的恐慌世舰,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件槽卫,死亡現(xiàn)場離奇詭異跟压,居然都是意外死亡,警方通過查閱死者的電腦和手機晒夹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門裆馒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人丐怯,你說我怎么就攤上這事喷好。” “怎么了读跷?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵梗搅,是天一觀的道長。 經(jīng)常有香客問我效览,道長无切,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任丐枉,我火速辦了婚禮哆键,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瘦锹。我一直安慰自己籍嘹,他們只是感情好,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布弯院。 她就那樣靜靜地躺著辱士,像睡著了一般。 火紅的嫁衣襯著肌膚如雪听绳。 梳的紋絲不亂的頭發(fā)上颂碘,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天,我揣著相機與錄音椅挣,去河邊找鬼头岔。 笑死,一個胖子當著我的面吹牛贴妻,可吹牛的內(nèi)容都是我干的切油。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼名惩,長吁一口氣:“原來是場噩夢啊……” “哼澎胡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤攻谁,失蹤者是張志新(化名)和其女友劉穎稚伍,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體戚宦,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡个曙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了受楼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垦搬。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖艳汽,靈堂內(nèi)的尸體忽然破棺而出猴贰,到底是詐尸還是另有隱情,我是刑警寧澤河狐,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布米绕,位于F島的核電站,受9級特大地震影響馋艺,放射性物質(zhì)發(fā)生泄漏栅干。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一捐祠、第九天 我趴在偏房一處隱蔽的房頂上張望碱鳞。 院中可真熱鬧,春花似錦踱蛀、人聲如沸劫笙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至戒洼,卻和暖如春俏橘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背圈浇。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工寥掐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人磷蜀。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓召耘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親褐隆。 傳聞我的和親對象是個殘疾皇子污它,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容