OkHttp 使用指南(四)--Recipes

  • Android
  • Square
  • OkHttp

OkHttp 使用指南(四)--Recipes

手打翻譯原文,若轉(zhuǎn)載本文請注明出處

?寫這篇文章之前是因?yàn)轫?xiàng)目想使用 OkHttp 網(wǎng)絡(luò)組件替換之前老是出問題的網(wǎng)絡(luò)組件燕雁,在集成之前最重要的是調(diào)研涡戳,參考的文獻(xiàn)主要也是官方提供的最新文檔醉锅,這篇文章也是我從官方文檔根據(jù)自己的理解翻譯過來勤庐,Recipes 這篇主要介紹的也是 OkHttp 的使用方法例子二鳄。
?我們使用 OkHttp 寫了很多方法例子來說明解決使用過程中遇到的常見的問題曙搬。通過閱讀這些例子這些組件是怎么在一起工作的摔吏。粘貼復(fù)制這些例子是自由免費(fèi)的鸽嫂,因?yàn)檫@就是這些例子的用途。

Synchronous Get(同步 GET)
?下載一個文件舔腾,以字符串的形式打印出他的頭部信息溪胶,打印出響應(yīng)數(shù)據(jù)體信息。
? String() 方法作為一些小文件的響應(yīng)數(shù)據(jù)體是非常方便和高效的稳诚。但是如果針對一些大文件的下載(大于 1MB 文件)哗脖,盡量避免使用 String() 方法因?yàn)樗麜⒄麄€文本加載到內(nèi)存中。針對這種例子優(yōu)先選擇的解決方案是將數(shù)據(jù)體作為一個數(shù)據(jù)流來處理扳还。

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

Asynchronous Get(異步 GET)
?在工作線程中進(jìn)行下載任務(wù)才避,并且在響應(yīng)到達(dá)的時候采用回調(diào)的方式通知。這個回調(diào)會等待響應(yīng)信息頭準(zhǔn)備好之后發(fā)送氨距,讀取這個響應(yīng)頭信息仍然會阻塞桑逝。目前的 OKHttp 不支持異步的 APIS 來接收處理部分的響應(yīng)體。

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();

    client.newCall(request).enqueue(new Callback() {
      @Override public void onFailure(Request request, IOException throwable) {
        throwable.printStackTrace();
      }

      @Override public void onResponse(Response response) throws IOException {
        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());
      }
    });
  }

Accessing Headers(訪問頭部)
?典型的 HTTP 頭部信息類似于 Map<String, String> :每一個屬性都有一個值或者沒有俏让,但是某些信息頭允許多個值信息楞遏,類似于 Guava Multimap。例如首昔,對于 Http 響應(yīng)信息頭部包含多個變量值是合法常見的寡喝。OkHttp APIs 計(jì)劃所有這些情況兼容。
?使用 header(name, value) 方法來寫唯一的請求頭部信息勒奇。如果已經(jīng)存在了一個值预鬓,在新的值添加前會移除掉當(dāng)前值。使用 addHeader(name, value) 方法增加頭部信息而不用移除之前已經(jīng)錄入的信息赊颠。
?通過 header(name) 讀取響應(yīng)頭部信息,返回的是最新的值格二,通常情況下都會返回值,但是如果本來就沒有的話會返回 null 竣蹦,通過使用 headers(name) 來訪問頭部信息顶猜。
?通過 headers 類可以根據(jù) index 來訪問頭部信息。

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    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();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println("Server: " + response.header("Server"));
    System.out.println("Date: " + response.header("Date"));
    System.out.println("Vary: " + response.headers("Vary"));
  }

Post a String(發(fā)送一個字符串?dāng)?shù)據(jù))
?使用 Http 以 Post 方式來發(fā)送一個請求到服務(wù)端草添,這個例子是發(fā)送一個 markdown 文件到服務(wù)端來渲染成一個 HTML驶兜,因?yàn)檎麄€請求是同時在內(nèi)存中的,在使用這個 Api 時盡量避免使用數(shù)據(jù)量較大远寸。

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 {
    String postBody = ""
        + "Releases\n"
        + "--------\n"
        + "\n"
        + " * _1.0_ May 6, 2013\n"
        + " * _1.1_ June 15, 2013\n"
        + " * _1.2_ August 11, 2013\n";

    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
  }

Post Streaming(Post 方式發(fā)送流式)
?這里我們要說明的例子是使用流式的方式發(fā)送請求抄淑。請求數(shù)據(jù)體的內(nèi)容已經(jīng)產(chǎn)生并被寫入。這個例子會將流直接寫入到 Okio 緩沖槽中驰后。你的項(xiàng)目可能會喜歡使用 OutputStream肆资,這個你可以從 BufferedSink.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());
  }

Posting a File(發(fā)送一個文件)
簡單使用file作為請求的數(shù)據(jù)體

 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 {
    File file = new File("README.md");

    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
  }

Posting form parameters (發(fā)送表單參數(shù))
?使用 FormEncodingBuilder 建立一個請求鏈接類似于 HTML <form> 標(biāo)簽灶芝。
鍵值對會被使用 HTML 響應(yīng)表單編碼規(guī)范來編碼郑原。

 private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    RequestBody formBody = new FormEncodingBuilder()
        .add("search", "Jurassic Park")
        .build();
    Request request = new Request.Builder()
        .url("https://en.wikipedia.org/w/index.php")
        .post(formBody)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
  }

Posting a multipart request(發(fā)送多塊請求)
?MultipartBuilder 用來組裝復(fù)雜的 HTML 文件上傳表單請求數(shù)據(jù)體唉韭。其中多塊請求數(shù)據(jù)體中的每一部分又是獨(dú)立的請求數(shù)據(jù)體,并且可以自己定義數(shù)據(jù)頭犯犁,如果這樣的話數(shù)據(jù)頭需要被定義成數(shù)據(jù)體的一部分属愤,比如說作為 Content-Disposition 如果是有效的數(shù)據(jù)體的話 Content-LengthContent-Length 會自動加上的。

 private static final String IMGUR_CLIENT_ID = "...";
  private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
    RequestBody requestBody = new MultipartBuilder()
        .type(MultipartBuilder.FORM)
        .addPart(
            Headers.of("Content-Disposition", "form-data; name=\"title\""),
            RequestBody.create(null, "Square Logo"))
        .addPart(
            Headers.of("Content-Disposition", "form-data; name=\"image\""),
            RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
        .build();

    Request request = new Request.Builder()
        .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
        .url("https://api.imgur.com/3/image")
        .post(requestBody)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
  }

Parse a JSON Response With Gson(通過 Gson 來解析 JSON 格式的響應(yīng)信息)

?Gson 是一種將 JSON 轉(zhuǎn)變?yōu)?JAVA 對象非常方便的 API酸役,這里我通過 Gson GitHub API 來解析對應(yīng) JSON 響應(yīng)數(shù)據(jù)住诸。
?需要注意的是 ResponseBody.charStream() 是使用相應(yīng)信息的頭部數(shù)據(jù) Content-Type 來選擇哪一種編碼來解析響應(yīng)的數(shù)據(jù)體。如果沒有指定編碼格式化默認(rèn)使用的是 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;
  }

Response Caching(響應(yīng)緩存)
?對于響應(yīng)緩存贱呐,你需要的是指定一個用來讀取和寫入的緩存存儲路徑,并且你需要指定一個緩存文件的大小限制入桂。這個緩存的路徑應(yīng)該是私有的奄薇,并且對于不收信任的應(yīng)用不能讀取緩存的內(nèi)容。
?如果多個緩存同時訪問同一個緩存地址的話會出現(xiàn)錯誤抗愁。大多數(shù)應(yīng)用調(diào)用 new OkHttpClient() 一次馁蒂,配置參數(shù)和緩存,在應(yīng)用的其他使用它的實(shí)例對象進(jìn)行操作蜘腌。否則兩個緩存對象會相互影響远搪,混亂的響應(yīng)緩可能會導(dǎo)致你的應(yīng)用崩潰。
?響應(yīng)緩存使用 HTTP headers 作為所有的配置信息逢捺。你可以在請求頭部增加 Cache-Control: max-stale=3600 這樣 OkHttpClient 就會執(zhí)行這個設(shè)置。你的服務(wù)端會響應(yīng)頭部使用自己的緩存時間配置信息癞季,例如 Cache-Control: max-age=9600 這些緩存頭部會強(qiáng)行拉取 緩存響應(yīng)劫瞳,網(wǎng)絡(luò)響應(yīng),網(wǎng)絡(luò)響應(yīng)會被刷新通過有條件的 GET 請求绷柒。

private final OkHttpClient client;

  public CacheResponse(File cacheDirectory) throws Exception {
    int cacheSize = 10 * 1024 * 1024; // 10 MiB
    Cache 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));
  }

?響應(yīng)不用緩存志于,可以使用 CacheControl.FORCE_NETWORK。響應(yīng)不用網(wǎng)絡(luò)废睦,使用 CacheControl.FORCE_CACHE伺绽。但是要注意的是如果你選擇使用緩存的話,響應(yīng)需要使用網(wǎng)絡(luò)的話嗜湃。OkHttp 會返回 504 Unsatisfiable Request 響應(yīng)奈应。

Canceling a Call(取消請求)
?使用 Call.cancel() 立即停止正在執(zhí)行的請求。如果一個線程正在執(zhí)行請求或者接收一個響應(yīng)的話购披,他會收到一個 IOException杖挣。可以使用這個方法來取消那些不在需要的網(wǎng)絡(luò)請求一邊節(jié)約網(wǎng)絡(luò)刚陡。比如用戶離開你的應(yīng)用惩妇,無論你的同步還是異步的請求就可以被取消掉株汉。
?你也可以通過使用 tag 來同時取消掉多個請求。在構(gòu)建一個請求的時候通過使用 RequestBuilder.tag(tag) 給請求打一個標(biāo)簽歌殃。然后通過 OkHttpClient.cancel(tag) 取消所有使用這個 tag 的請求乔妈。

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

Timeouts(超時)
?使用超時機(jī)制來取消一個另一個端點(diǎn)不可達(dá)的狀態(tài)。網(wǎng)絡(luò)模塊可以劃分為客戶端連接問題氓皱、服務(wù)端可用問題或者兩者都有路召,OkHttp 支持連接 read 和 write 三個時間的響應(yīng)。

  private final OkHttpClient client;

  public ConfigureTimeouts() throws Exception {
    client = new OkHttpClient();
    client.setConnectTimeout(10, TimeUnit.SECONDS);
    client.setWriteTimeout(10, TimeUnit.SECONDS);
    client.setReadTimeout(30, TimeUnit.SECONDS);
  }

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

Per-call Configuration(單次配置)
?所有的 Http 客戶端配置都是在 OkHttpClient 設(shè)置包括代理設(shè)置匀泊、超時和緩存优训。當(dāng)你需要單次配置客戶端的時候話可以 clone OkHttpClient 這個將會返回一個淺 copy 對象這樣你就可以定制單獨(dú)的客戶端設(shè)置了。下面的例子你可以看到一個設(shè)置 500ms 的超時和一個 30000ms 超時時間各聘。

 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 {
      OkHttpClient cloned = client.clone(); // Clone to make a customized OkHttp for this request.
      cloned.setReadTimeout(500, TimeUnit.MILLISECONDS);

      Response response = cloned.newCall(request).execute();
      System.out.println("Response 1 succeeded: " + response);
    } catch (IOException e) {
      System.out.println("Response 1 failed: " + e);
    }

    try {
      OkHttpClient cloned = client.clone(); // Clone to make a customized OkHttp for this request.
      cloned.setReadTimeout(3000, TimeUnit.MILLISECONDS);

      Response response = cloned.newCall(request).execute();
      System.out.println("Response 2 succeeded: " + response);
    } catch (IOException e) {
      System.out.println("Response 2 failed: " + e);
    }
  }

Handling authentication(處理證書)
?OkHttp 可以自動重試非證書的請求揣非,當(dāng)一個請求的響應(yīng)是 401 Not AuthorizedAuthenticator 被要求提供證書驗(yàn)證躲因,需要重新執(zhí)行一個包含證書的請求早敬。如果沒有證書可以提供會返回空來跳過重試。使用 Response.challenges() 獲得一個模式和領(lǐng)域來針對所有的證書驗(yàn)證大脉,當(dāng)面對的是一個 Basic 驗(yàn)證搞监。使用 Credentials.basic(username, password) 來編碼請求信息。

 private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    client.setAuthenticator(new Authenticator() {
      @Override public Request authenticate(Proxy proxy, Response response) {
        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();
      }

      @Override public Request authenticateProxy(Proxy proxy, Response response) {
        return null; // Null indicates no attempt to authenticate.
      }
    });

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

?為了避免驗(yàn)證失敗出現(xiàn)的多次重試镰矿,你可以通過返回 null 來跳過琐驴。例如你可以跳過重試當(dāng)你遇到這些證書被要求驗(yàn)證。

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

?你也可以通過設(shè)置重試次數(shù)限制來跳過重試請求秤标。

if (responseCount(response) >= 3) {
    return null; // If we've failed 3 times, give up.
  }

上述代碼依賴 responseCount() 方法

private int responseCount(Response response) {
    int result = 1;
    while ((response = response.priorResponse()) != null) {
      result++;
    }
    return result;
  }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末绝淡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子苍姜,更是在濱河造成了極大的恐慌牢酵,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件衙猪,死亡現(xiàn)場離奇詭異馍乙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)垫释,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門丝格,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人饶号,你說我怎么就攤上這事铁追。” “怎么了茫船?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵琅束,是天一觀的道長扭屁。 經(jīng)常有香客問我,道長涩禀,這世上最難降的妖魔是什么料滥? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮艾船,結(jié)果婚禮上葵腹,老公的妹妹穿的比我還像新娘。我一直安慰自己屿岂,他們只是感情好践宴,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著爷怀,像睡著了一般阻肩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上运授,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天烤惊,我揣著相機(jī)與錄音,去河邊找鬼吁朦。 笑死柒室,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的逗宜。 我是一名探鬼主播雄右,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼纺讲!你這毒婦竟也來了不脯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤刻诊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后牺丙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體则涯,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年冲簿,在試婚紗的時候發(fā)現(xiàn)自己被綠了粟判。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡峦剔,死狀恐怖档礁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吝沫,我是刑警寧澤呻澜,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布递礼,位于F島的核電站,受9級特大地震影響羹幸,放射性物質(zhì)發(fā)生泄漏脊髓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一栅受、第九天 我趴在偏房一處隱蔽的房頂上張望将硝。 院中可真熱鬧,春花似錦屏镊、人聲如沸依疼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽律罢。三九已至,卻和暖如春蔚出,著一層夾襖步出監(jiān)牢的瞬間弟翘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工骄酗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留稀余,地道東北人。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓趋翻,卻偏偏與公主長得像睛琳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子踏烙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

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

  • OkHttp使用完全教程 標(biāo)簽 : Http請求, 類庫blog : http://blog.csdn.net/o...
    oncealong閱讀 170,131評論 27 459
  • OkHttp解析系列 OkHttp解析(一)從用法看清原理OkHttp解析(二)網(wǎng)絡(luò)連接OkHttp解析(三)關(guān)于...
    Hohohong閱讀 20,971評論 4 58
  • 那么我今天給大家簡單地講一下Okhttp這款網(wǎng)絡(luò)框架及其原理师骗。它是如何請求數(shù)據(jù),如何響應(yīng)數(shù)據(jù)的 有什么優(yōu)點(diǎn)讨惩?它的應(yīng)...
    卓而不群_0137閱讀 313評論 0 1
  • 介紹 OkHttp是一個用于Android和Java應(yīng)用程序的HTTP客戶端辟癌。相關(guān)參數(shù)和配置都是用Builder模...
    m1Ku閱讀 1,350評論 0 1
  • Android中OkHttp的使用 官方網(wǎng)站 | Javadoc 1 簡介 OkHttp是Square出的Http...
    LuckyXiang閱讀 9,634評論 0 13