www.icesr.com
IT运维工程师的摇篮

okhttp使用总结

okhttp 使用总结

okhttp 的使用越来越火,有必要对其进行研究。以下博客中的例子为了简单,在Eclipse中通过JAVA工程进行验证。

导入OkHttp

AndroidStudio

添加如下代码即可

<code> compile <span class="hljs-string">'com.squareup.okhttp3:okhttp:3.3.1'</span>
 compile <span class="hljs-string">'com.squareup.okio:okio:1.8.0'</span>
</code>

其中okiookhttp中关于流的操作。必须导入。

Eclipse

导入okhttpokio的jar包即可。

下载地址okhttp3.3.1与okio-1.8.0

同步的GET请求

主要分为以下几步:

  • 创建OkHttpClient对象
  • 根据需求创建Request对象,Request中主要包含了请求的url,参数等信息。
  • 通过request对象创建Call对象
  • 使用call对象进行网络请求。返回封装好的Response
  • 解析Response对象,获取响应信息
<code><span class="hljs-javadoc">/**
*  同步 Get 请求
*/</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">syncGet</span>() <span class="hljs-keyword">throws</span> Exception {

        <span class="hljs-comment">// 1. 创建`OkHttpClient`对象</span>
        <span class="hljs-keyword">final</span> OkHttpClient client = <span class="hljs-keyword">new</span> OkHttpClient();

        <span class="hljs-comment">// 2.根据需求创建`Request`对象.在此只是添加了最基本的url</span>
        Request request = <span class="hljs-keyword">new</span> Request.Builder().url(BAIDU_MP3_PATH).build();

        <span class="hljs-comment">// 3.通过`request`对象创建`Call`对象</span>
        Call call = client.newCall(request);

        <span class="hljs-comment">// 4.使用`call`对象进行网络请求。返回封装好的`Response`</span>
        Response response = call.execute();

        <span class="hljs-comment">// 5.解析`Response`对象,获取响应信息</span>
        <span class="hljs-keyword">if</span> (!response.isSuccessful())
            <span class="hljs-comment">// code &gt;= 200 &amp;&amp; code &lt; 300;</span>
            System.out.println(response);

        Headers responseHeaders = response.headers();

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; responseHeaders.size(); i++) {
            System.out.println(responseHeaders.name(i) + <span class="hljs-string">":"</span>
                    + responseHeaders.value(i));
        }

        <span class="hljs-comment">// 打印响应的内容</span>
        System.out.println(response.body().string());

    }
</code>

其中使用的url为百度MP3的接口:

<code>http:<span class="hljs-comment">//tingapi.ting.baidu.com/v1/restserver/ting?from=qianqian&amp;version=2.1.0&amp;method=baidu.ting.billboard.billList&amp;format=json&amp;type=1&amp;offset=0&amp;size=1</span></code>

看一下打印结果:

<code>
<span class="hljs-comment">//---- response 的toString() 默认打印的信息  响应行</span>
Response{protocol=http/<span class="hljs-number">1.1</span>, code=<span class="hljs-number">200</span>, message=OK, url=http:<span class="hljs-comment">//tingapi.ting.baidu.com/v1/restserver/ting?from=qianqian&amp;version=2.1.0&amp;method=baidu.ting.billboard.billList&amp;format=json&amp;type=1&amp;offset=0&amp;size=1}</span>


<span class="hljs-comment">//----- 响应的头</span>
Date:Wed, <span class="hljs-number">29</span> Jun <span class="hljs-number">2016</span> <span class="hljs-number">01</span>:<span class="hljs-number">51</span>:<span class="hljs-number">39</span> GMT
Content-Type:application/json
Transfer-Encoding:chunked
Connection:keep-alive
X-LIGHTTPD-LOGID:<span class="hljs-number">427084050</span>
RT:<span class="hljs-number">427084050</span>_d41d8cd98f00b204e9800998ecf8427e_d41d8cd98f00b204e9800998ecf8427e
Server:Apache1<span class="hljs-number">.0</span>
SC:MISS<span class="hljs-number">.0</span><span class="hljs-number">.0</span>
tracecode:<span class="hljs-number">30991485713674384576062909</span>
Set-Cookie:BAIDUID=B343E34EB3618E7E2E867A6E1D4F04EB:FG=<span class="hljs-number">1</span>; expires=Thu, <span class="hljs-number">29</span>-Jun-<span class="hljs-number">17</span> <span class="hljs-number">01</span>:<span class="hljs-number">51</span>:<span class="hljs-number">39</span> GMT; max-age=<span class="hljs-number">31536000</span>; path=/; domain=.baidu.com; version=<span class="hljs-number">1</span>
P3P:CP=<span class="hljs-string">" OTI DSP COR IVA OUR IND COM "</span>


<span class="hljs-comment">// ---- 响应的内容  body</span>
{<span class="hljs-string">"song_list"</span>:[{<span class="hljs-string">"artist_id"</span>:<span class="hljs-string">"247010469"</span>,<span class="hljs-string">"language"</span>:<span class="hljs-string">"/u56fd/u8bed"</span>,<span class="hljs-string">"pic_big"</span>:<span class="hljs-string">"http:////musicdata.baidu.com//data2//pic//0c87cb20fd6b543cd1c90d1d82f1f6c5//266782982//266782982.jpg"</span>,<span class="hljs-string">"pic_small"</span>:<span class="hljs-string">"http:////musicdata.baidu.com//data2//pic//169cbb0d82522568dea198363f7028ec//266782985//266782985.jpg"</span>,<span class="hljs-string">"country"</span>:<span class="hljs-string">"/u5185/u5730"</span>,<span class="hljs-string">"area"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"publishtime"</span>:<span class="hljs-string">"2016-06-20"</span>,<span class="hljs-string">"album_no"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"lrclink"</span>:<span class="hljs-string">"http:////musicdata.baidu.com//data2//lrc//1f75306d7f1e25f723d3d175f5ef3d46//266791084//266791084.lrc"</span>,<span class="hljs-string">"copy_type"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"hot"</span>:<span class="hljs-string">"276927"</span>,<span class="hljs-string">"all_artist_ting_uid"</span>:<span class="hljs-string">"232954914"</span>,<span class="hljs-string">"resource_type"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"is_new"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"rank_change"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"rank"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"all_artist_id"</span>:<span class="hljs-string">"247010469"</span>,<span class="hljs-string">"style"</span>:<span class="hljs-string">"/u6d41/u884c"</span>,<span class="hljs-string">"del_status"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"relate_status"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"toneid"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"all_rate"</span>:<span class="hljs-string">"64,128,256"</span>,<span class="hljs-string">"sound_effect"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"file_duration"</span>:<span class="hljs-number">0</span>,<span class="hljs-string">"has_mv_mobile"</span>:<span class="hljs-number">1</span>,<span class="hljs-string">"versions"</span>:<span class="hljs-string">""</span>,<span class="hljs-string">"bitrate_fee"</span>:<span class="hljs-string">"{/"0/":/"0|0/",/"1/":/"-1|-1/"}"</span>,<span class="hljs-string">"song_id"</span>:<span class="hljs-string">"266784254"</span>,<span class="hljs-string">"title"</span>:<span class="hljs-string">"/u7ea2"</span>,<span class="hljs-string">"ting_uid"</span>:<span class="hljs-string">"232954914"</span>,<span class="hljs-string">"author"</span>:<span class="hljs-string">"/u51af/u5efa/u5b87"</span>,<span class="hljs-string">"album_id"</span>:<span class="hljs-string">"266784256"</span>,<span class="hljs-string">"album_title"</span>:<span class="hljs-string">"/u7ea2"</span>,<span class="hljs-string">"is_first_publish"</span>:<span class="hljs-number">0</span>,<span class="hljs-string">"havehigh"</span>:<span class="hljs-number">0</span>,<span class="hljs-string">"charge"</span>:<span class="hljs-number">0</span>,<span class="hljs-string">"has_mv"</span>:<span class="hljs-number">1</span>,<span class="hljs-string">"learn"</span>:<span class="hljs-number">0</span>,<span class="hljs-string">"song_source"</span>:<span class="hljs-string">"web"</span>,<span class="hljs-string">"piao_id"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"korean_bb_song"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"resource_type_ext"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"mv_provider"</span>:<span class="hljs-string">"0100000000"</span>,<span class="hljs-string">"artist_name"</span>:<span class="hljs-string">"/u51af/u5efa/u5b87"</span>}],<span class="hljs-string">"billboard"</span>:{<span class="hljs-string">"billboard_type"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"billboard_no"</span>:<span class="hljs-string">"1871"</span>,<span class="hljs-string">"update_date"</span>:<span class="hljs-string">"2016-06-28"</span>,<span class="hljs-string">"billboard_songnum"</span>:<span class="hljs-string">"190"</span>,<span class="hljs-string">"havemore"</span>:<span class="hljs-number">1</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"/u65b0/u6b4c/u699c"</span>,<span class="hljs-string">"comment"</span>:<span class="hljs-string">"/u8be5/u699c/u5355/u662f/u6839/u636e/u767e/u5ea6/u97f3/u4e50/u5e73/u53f0/u6b4c/u66f2/u6bcf/u65e5/u64ad/u653e/u91cf/u81ea/u52a8/u751f/u6210/u7684/u6570/u636e/u699c/u5355/uff0c/u7edf/u8ba1/u8303/u56f4/u4e3a/u8fd1/u671f/u53d1/u884c/u7684/u6b4c/u66f2/uff0c/u6bcf/u65e5/u66f4/u65b0/u4e00/u6b21"</span>,<span class="hljs-string">"pic_s640"</span>:<span class="hljs-string">"http:////c.hiphotos.baidu.com//ting//pic//item//f7246b600c33874495c4d089530fd9f9d62aa0c6.jpg"</span>,<span class="hljs-string">"pic_s444"</span>:<span class="hljs-string">"http:////d.hiphotos.baidu.com//ting//pic//item//78310a55b319ebc4845c84eb8026cffc1e17169f.jpg"</span>,<span class="hljs-string">"pic_s260"</span>:<span class="hljs-string">"http:////b.hiphotos.baidu.com//ting//pic//item//e850352ac65c1038cb0f3cb0b0119313b07e894b.jpg"</span>,<span class="hljs-string">"pic_s210"</span>:<span class="hljs-string">"http:////business.cdn.qianqian.com//qianqian//pic//bos_client_c49310115801d43d42a98fdc357f6057.jpg"</span>,<span class="hljs-string">"web_url"</span>:<span class="hljs-string">"http:////music.baidu.com//top//new"</span>},<span class="hljs-string">"error_code"</span>:<span class="hljs-number">22000</span>}

</code>

如果熟悉HTTP 协议的可知,作为Http的响应,分为响应行,响应头,响应体。

如果不熟悉HTTP协议,欢迎看我之前的博客Java Web之HTTP协议总结

如上打印结果就分别对应了响应行,响应头,响应体。

注释很清楚,按照步骤即可,着重提一下解析Response的内容。

  • response.isSuccessful(),该方法在响应code为[200,300)时,返回true,其余返回false。注意区间为前闭后开。
  • response.headers():获取响应行信息的所有参数的集合headersheaders的使用方式类似于List
  • response.body().string():该方法返回的是响应的内容。但是,官方给出的指导是,如果响应的内容大于1MB 时,不建议使用该方法。因为该方法会将内容全部加载到内存中。推荐使用流的方式获取加载的数据。
    • response.body().byteStream();:字节输入流
    • response.body().charStream();:字符输入流

异步的GET 请求

异步GET的请求方式几乎和上面的步骤类似,唯一的区别是通过Call对象请求网络时,不在直接返回Response对象,而是通过接口回调的方式获取数据。当然,表面上是这样,其内部肯定有对应的线程切换。

<code><span class="hljs-javadoc">/**
     * 异步的get方法
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">asyncGet</span>() <span class="hljs-keyword">throws</span> Exception {

        <span class="hljs-comment">// 1. 创建`OkHttpClient`对象</span>
        <span class="hljs-keyword">final</span> OkHttpClient client = <span class="hljs-keyword">new</span> OkHttpClient();

        <span class="hljs-comment">// 2.根据需求创建`Request`对象.在此只是添加了最基本的url</span>
        Request request = <span class="hljs-keyword">new</span> Request.Builder().url(BAIDU_MP3_PATH).build();

        <span class="hljs-comment">// 3.通过`request`对象创建`Call`对象</span>
        Call call = client.newCall(request);

        <span class="hljs-comment">// 4.使用`call`对象进行网络请求。通过Callback 监听网络请求</span>
        call.enqueue(<span class="hljs-keyword">new</span> Callback() {

            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onResponse</span>(Call call, Response response)
                    <span class="hljs-keyword">throws</span> IOException {
                <span class="hljs-comment">//5, 获取响应结果并解析</span>
                <span class="hljs-keyword">if</span> (!response.isSuccessful()) {
                    System.out.println(response);
                    <span class="hljs-keyword">return</span>;
                }

                Headers responseHeaders = response.headers();
                <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>, size = responseHeaders.size(); i &lt; size; i++) {
                    System.out.println(responseHeaders.name(i) + <span class="hljs-string">": "</span>
                            + responseHeaders.value(i));
                }

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

            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onFailure</span>(Call call, IOException e) {
                System.out.println(e);
            }

        });
    }
</code>

此时我们使用了call.enqueue()方法,并通过Callback对象监听回调,其中Callback对象中包含了两个回调方法,成功onResponse和失败onFailure

肯定会有人说,我擦~~onResponse()中为什么还要判断response.isSuccessful(),不是已经成功了吗。

在这里这两个方法的含义和其他框架的有区别。

在这贴上该方法的注释就明白了

<code>
    <span class="hljs-javadoc">/**
   * Called when the request could not be executed due to cancellation, a connectivity problem or
   * timeout. Because networks can fail during an exchange, it is possible that the remote server
   * accepted the request before the failure.
   */</span>
  <span class="hljs-keyword">void</span> onFailure(Call call, IOException e);

  <span class="hljs-javadoc">/**
   * Called when the HTTP response was successfully returned by the remote server. The callback may
   * proceed to read the response body with {@link Response#body}. The response is still live until
   * its response body is {@linkplain ResponseBody closed}. The recipient of the callback may
   * consume the response body on another thread.
   *
   * &lt;p&gt;Note that transport-layer success (receiving a HTTP response code, headers and body) does
   * not necessarily indicate application-layer success: {@code response} may still indicate an
   * unhappy HTTP response code like 404 or 500.
   */</span>
  <span class="hljs-keyword">void</span> onResponse(Call call, Response response) <span class="hljs-keyword">throws</span> IOException;
</code>

有能力的自己翻译,我是这样理解的。

  • onResponse() :连接上了服务器,不管服务器是否有资源(404)或者服务器出bug了(500)。
  • onFailure(): 没有连接上服务器。(域名不存在,无网络。。。)

设置和获取响应行参数(Header)

HTTP 协议中,无论对请求和响应,都有相应的头参数。一些特别的参数能够完成一些特别的任务。类似缓存,传输编码,重定向等等。

我们知道头信息中的参数类似于Map<String,String>,键值对的形式,但是有些特殊的头,不是一对一的,而是一对多。那么此时okHttp肯定要有特别的方法获取这些信息。

<code><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">accessHeader</span>() <span class="hljs-keyword">throws</span> Exception {
        <span class="hljs-keyword">final</span> OkHttpClient client = <span class="hljs-keyword">new</span> OkHttpClient();

        Request request = <span class="hljs-keyword">new</span> Request.Builder()
                .url(<span class="hljs-string">"https://api.github.com/repos/square/okhttp/issues"</span>)
                <span class="hljs-comment">// 只有一个参数的头  ,一对一,如果在添加会覆盖</span>
                .header(<span class="hljs-string">"User-Agent"</span>, <span class="hljs-string">"OkHttp Headers.java"</span>)
                <span class="hljs-comment">// 一对多,添加的关系</span>
                .addHeader(<span class="hljs-string">"Accept"</span>, <span class="hljs-string">"application/json; q=0.5"</span>)
                .addHeader(<span class="hljs-string">"Accept"</span>, <span class="hljs-string">"application/vnd.github.v3+json"</span>).build();

        Response response = client.newCall(request).execute();
        <span class="hljs-keyword">if</span> (!response.isSuccessful())
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IOException(<span class="hljs-string">"Unexpected code "</span> + response);

        System.out.println(<span class="hljs-string">"Server: "</span> + response.header(<span class="hljs-string">"Server"</span>));
        System.out.println(<span class="hljs-string">"Date: "</span> + response.header(<span class="hljs-string">"Date"</span>));
        <span class="hljs-comment">// 一对多的获取</span>
        System.out.println(<span class="hljs-string">"Vary: "</span> + response.headers(<span class="hljs-string">"Vary"</span>));
    }
</code>

添加请求头信息时分为两种情况:
– 一对一: 使用header(String key,String value)方法,相同的key值调用此方法,会覆盖之前的value。
– 一对多: 使用addHeader(String key,String value)方法,相同的key值调用此方法,执行追加操作。

获取响应头信息时:

  • 一对一: 使用header(String key)方法。
  • 一对多: 使用headers(String key)方法。

POST 请求,传入一个字符串

Post请求,在构造Request时,传入POST的参数即可。

<code><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">postString</span>() <span class="hljs-keyword">throws</span> Exception {

        <span class="hljs-comment">// post 传参类型 编码</span>
        <span class="hljs-keyword">final</span> MediaType MEDIA_TYPE_MARKDOWN = MediaType
                .parse(<span class="hljs-string">"text/x-markdown; charset=utf-8"</span>);
        <span class="hljs-comment">// post 参数值</span>
        String params = <span class="hljs-string">"### 三级标题"</span>;

        <span class="hljs-keyword">final</span> OkHttpClient client = <span class="hljs-keyword">new</span> OkHttpClient();
        Request request = <span class="hljs-keyword">new</span> Request.Builder()
                .url(<span class="hljs-string">"https://api.github.com/markdown/raw"</span>)
                .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, params)).build();

        Response response = client.newCall(request).execute();
        <span class="hljs-keyword">if</span> (!response.isSuccessful())
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IOException(<span class="hljs-string">"Unexpected code "</span> + response);

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

    }
</code>

Request.post中传入的是RequestBody对象,该对象的创建需要两个参数

  • MediaType contentType:传入参数的类型。通常使用的是text/html; chareset=utf-8。明白此值的含义,其实就是理解HTTP 协议中content-type的作用:决定浏览器/服务器将以什么形式、什么编码读取这个文件。
  • String content:post发送的内容。

在此,参数类型使用的是text/x-markdown; charset=utf-8,因为是调用github 的markdown的解析服务。所以传入了特定的类型。

返回结果

<code>&lt;h3&gt;
&lt;a id=<span class="hljs-string">"user-content-三级标题"</span> class=<span class="hljs-string">"anchor"</span> href=<span class="hljs-string">"#%E4%B8%89%E7%BA%A7%E6%A0%87%E9%A2%98"</span> aria-hidden=<span class="hljs-string">"true"</span>&gt;&lt;span aria-hidden=<span class="hljs-string">"true"</span> class=<span class="hljs-string">"octicon octicon-link"</span>&gt;&lt;/span&gt;&lt;/a&gt;三级标题&lt;/h3&gt;</code>

可见已经解析成了html 语言。

POST 文件

传入文件就是在RequestBody.creat()中,第二个参数传入一个file对象。

<code> <span class="hljs-javadoc">/**
     * 传入文件
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">postFile</span>() <span class="hljs-keyword">throws</span> Exception{
        <span class="hljs-keyword">final</span> MediaType MEDIA_TYPE_MARKDOWN = MediaType
                .parse(<span class="hljs-string">"text/x-markdown; charset=utf-8"</span>);

        <span class="hljs-keyword">final</span> OkHttpClient client = <span class="hljs-keyword">new</span> OkHttpClient();

        Request request = <span class="hljs-keyword">new</span> Request.Builder()
                .url(<span class="hljs-string">"https://api.github.com/markdown/raw"</span>)
                .post(RequestBody.create(MEDIA_TYPE_MARKDOWN,<span class="hljs-keyword">new</span> File(<span class="hljs-string">"README.MD"</span>)))
                .build();

        <span class="hljs-comment">// ....</span>
    }
</code>

看看create方法,对于文件干了什么。

<code>    <span class="hljs-keyword">new</span> RequestBody() {
      <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> MediaType <span class="hljs-title">contentType</span>() {
        <span class="hljs-keyword">return</span> contentType;
      }

      <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">long</span> <span class="hljs-title">contentLength</span>() {
        <span class="hljs-keyword">return</span> file.length();
      }

      <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">writeTo</span>(BufferedSink sink) <span class="hljs-keyword">throws</span> IOException {
        Source source = <span class="hljs-keyword">null</span>;
        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// ---- 读取文件</span>
          source = Okio.source(file);
          sink.writeAll(source);
        } <span class="hljs-keyword">finally</span> {
          Util.closeQuietly(source);
        }
      }
    };
</code>

该方法中,根据传入的参数创建了一个RequestBody对象,其中writeTo中读取了文件,source = Okio.source(file);square封装的io操作库。继续深入

<code><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Source <span class="hljs-title">source</span>(File file) <span class="hljs-keyword">throws</span> FileNotFoundException {
    <span class="hljs-keyword">if</span> (file == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"file == null"</span>);
    <span class="hljs-comment">// --- 字节输入流,读取文件</span>
    <span class="hljs-keyword">return</span> source(<span class="hljs-keyword">new</span> FileInputStream(file));
  }</code>

看到这里,就明白了,其实就是通过字节输入流,去读取文件。中间有加了基层流的转换,暂时不管。

POST form 表单(日常使用)

如上的例子中,使用RequestBody.create()方法作为post的参数,很是麻烦。如果对于日常使用的post请求,需要些很多代码。那么理所当然的会有其子类替我们封装了这些方法。

FormBodyReqeustBody的子类,主要用于常见的表单操作。

<code>    <span class="hljs-javadoc">/**
     * post form 表单
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">postForm</span>(){

        RequestBody formBody = <span class="hljs-keyword">new</span> FormBody.Builder()
                .add(<span class="hljs-string">"name"</span>,<span class="hljs-string">"1234"</span>)
                .build();

        <span class="hljs-keyword">final</span> OkHttpClient client = <span class="hljs-keyword">new</span> OkHttpClient();

        Request request = <span class="hljs-keyword">new</span> Request.Builder()
                .url(<span class="hljs-string">"https://api.github.com/markdown/raw"</span>)
                .post(formBody)
                .build();

        <span class="hljs-comment">// .....</span>
    }
</code>

通过add(String key,String value)方法添加参数即可。

POST 实现复杂的form表单提交(上传文件)

在开发,往往会有上传用户头像等的需求,即复杂的表单。此时可以使用RequestBody的子类MultipartBody实现。

<code> <span class="hljs-javadoc">/**
     * 提交复杂的form 表单 附带文件
     *
     *  MultiBody
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">postMultipart</span>(){

        <span class="hljs-javadoc">/**
         * 上传的文件类型
         */</span>
        <span class="hljs-keyword">final</span> MediaType MEDIA_TYPE_PNG = MediaType.parse(<span class="hljs-string">"image/png"</span>);

        <span class="hljs-javadoc">/**
         * 参数
         */</span>
        RequestBody requestBody = <span class="hljs-keyword">new</span> MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart(<span class="hljs-string">"title"</span>,<span class="hljs-string">"标题"</span>)
                .addFormDataPart(<span class="hljs-string">"image"</span>,<span class="hljs-string">"logo.png"</span>,RequestBody.create(MEDIA_TYPE_PNG,<span class="hljs-keyword">new</span> File(<span class="hljs-string">"xx.png"</span>)))
                .build();

        Request request = <span class="hljs-keyword">new</span> Request.Builder()
                .url(<span class="hljs-string">"xxx"</span>)
                .post(requestBody)
                .build();

        <span class="hljs-comment">// ......</span>
    }
</code>

Cache 缓存的实现

OkHttpClient可以实现缓存,但如果我们不做任何配置(设置缓存的目录),则是没有缓存的。

看下面的例子

<code>        OkHttpClient client = <span class="hljs-keyword">new</span> OkHttpClient.Builder().build();

        Request request = <span class="hljs-keyword">new</span> Request.Builder().url(
                <span class="hljs-string">"http://xxxx/login.txt"</span>)
                <span class="hljs-comment">//.cacheControl(new CacheControl.Builder().noCache().build())</span>
                .build();

        Response response1 = client.newCall(request).execute();

        String response1Body = response1.body().string();
        <span class="hljs-comment">// 响应的结果信息</span>
        System.out.println(<span class="hljs-string">"Response 1 response:          "</span> + response1);

        <span class="hljs-comment">// 缓存的响应信息</span>
        System.out.println(<span class="hljs-string">"Response 1 cache response:    "</span>
                + response1.cacheResponse());
        <span class="hljs-comment">// 网络请求的响应信息</span>
        System.out.println(<span class="hljs-string">"Response 1 network response:  "</span>
                + response1.networkResponse());

        <span class="hljs-comment">// ************第二次请求******************  </span>
        Response response2 = client.newCall(request).execute();

        String response2Body = response2.body().string();
        System.out.println(<span class="hljs-string">"Response 2 response:          "</span> + response2);
        System.out.println(<span class="hljs-string">"Response 2 cache response:    "</span>
                + response2.cacheResponse());
        System.out.println(<span class="hljs-string">"Response 2 network response:  "</span>
                + response2.networkResponse());

        System.out.println(<span class="hljs-string">"Response 2 equals Response 1? "</span>
                + response1Body.equals(response2Body));
</code>

进行了两次请求,在请求之后,分别打印三个结果
response:最终的响应
cacheResponse: 缓存的响应,如果是从缓冲中取的,则该值不为null。
networkResponse:网络请求的响应,如果是从网络中请求的,则该值不为null。

<code>Response <span class="hljs-number">1</span> response:          Response{protocol=http/<span class="hljs-number">1.1</span>, code=<span class="hljs-number">200</span>, message=OK, url=http:<span class="hljs-comment">//xxx/login.txt}</span>
Response <span class="hljs-number">1</span> cache response:    <span class="hljs-keyword">null</span>
Response <span class="hljs-number">1</span> network response:  Response{protocol=http/<span class="hljs-number">1.1</span>, code=<span class="hljs-number">200</span>, message=OK, url=http:<span class="hljs-comment">//xxx/login.txt}</span>
Response <span class="hljs-number">2</span> response:          Response{protocol=http/<span class="hljs-number">1.1</span>, code=<span class="hljs-number">200</span>, message=OK, url=http:<span class="hljs-comment">//xxx/login.txt}</span>
Response <span class="hljs-number">2</span> cache response:    <span class="hljs-keyword">null</span>
Response <span class="hljs-number">2</span> network response:  Response{protocol=http/<span class="hljs-number">1.1</span>, code=<span class="hljs-number">200</span>, message=OK, url=http:<span class="hljs-comment">//xxx/login.txt}</span>
Response <span class="hljs-number">2</span> equals Response <span class="hljs-number">1</span>? <span class="hljs-keyword">true</span></code>

会发现cache response一直为null,而network response 一直不为null。可见使用了缓存。

修改代码,添加缓存

<code>        <span class="hljs-comment">// 缓存的大小</span>
        <span class="hljs-keyword">int</span> cacheSize = <span class="hljs-number">10</span> * <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span>; <span class="hljs-comment">// 10 MiB</span>
        <span class="hljs-comment">// 缓存对象,第一个参数为缓存的目录</span>
        Cache cache = <span class="hljs-keyword">new</span> Cache(<span class="hljs-keyword">new</span> File(<span class="hljs-string">"cache"</span>), cacheSize);
        <span class="hljs-comment">// 设置缓存</span>
        OkHttpClient client = <span class="hljs-keyword">new</span> OkHttpClient.Builder().cache(cache).build();

        <span class="hljs-comment">//.....</span></code>

通过构造Cache对象并对OkHttpClient设置。

<code>Response <span class="hljs-number">1</span> response:          Response{protocol=http/<span class="hljs-number">1.1</span>, code=<span class="hljs-number">200</span>, message=OK, url=http:<span class="hljs-comment">//xxx/login.txt}</span>
Response <span class="hljs-number">1</span> cache response:    <span class="hljs-keyword">null</span>
Response <span class="hljs-number">1</span> network response:  Response{protocol=http/<span class="hljs-number">1.1</span>, code=<span class="hljs-number">200</span>, message=OK, url=http:<span class="hljs-comment">//xxx/login.txt}</span>
Response <span class="hljs-number">2</span> response:          Response{protocol=http/<span class="hljs-number">1.1</span>, code=<span class="hljs-number">200</span>, message=OK, url=http:<span class="hljs-comment">//xxx/login.txt}</span>
Response <span class="hljs-number">2</span> cache response:    Response{protocol=http/<span class="hljs-number">1.1</span>, code=<span class="hljs-number">200</span>, message=OK, url=http:<span class="hljs-comment">//xxx/login.txt}</span>
Response <span class="hljs-number">2</span> network response:  <span class="hljs-keyword">null</span>
Response <span class="hljs-number">2</span> equals Response <span class="hljs-number">1</span>? <span class="hljs-keyword">true</span></code>

第一次请求时,缓存为null,网络请求不为null。因为初次加载,并没有缓存。

第二次时,网络请求为null。缓存不为null。直接从之前保存的缓存中获取值。

当然。此时是对所有的request设置是否缓存,同时我们可以对每一个request设置独属于自己的缓存。

<code>        <span class="hljs-comment">// 不缓存</span>
        Request request = <span class="hljs-keyword">new</span> Request.Builder().url(
                <span class="hljs-string">"http://xxx/login.txt"</span>)
                .cacheControl(<span class="hljs-keyword">new</span> CacheControl.Builder().noCache().build())
                .build();</code>

通过cacheControl()传入一个缓存的配置对象,

  • 不使用缓存:new CacheControl.Builder().noCache().build()
  • 设置缓存的时长:new CacheControl.Builder().maxAge().build()
    • maxAge(int maxAge, TimeUnit timeUnit): 时间数值,单位。

取消网络请求

当一个页面结束时,往往要结束网络请求,此时可以通过Call.cancel()取消一个网络请求。这样做有利于节省系统资源。如果结束时,当前正在请求或响应,则会抛出一个IOException

也可以取消多个网络请求。

当构建请求时,使用RequestBuilder.tag(tag)对当前请求设置一个标签。之后通过OkHttpClient.cancel(tag)取消对应标签的请求。

超时的设置

连接超时,写入超时,读取超时是网络访问中常见的超时。

<code>OkHttpClient  client = <span class="hljs-keyword">new</span> OkHttpClient.Builder()
                .connectTimeout(<span class="hljs-number">10</span>, TimeUnit.SECONDS)<span class="hljs-comment">// 连接超时 10s</span>
                .writeTimeout(<span class="hljs-number">10</span>, TimeUnit.SECONDS) <span class="hljs-comment">// 输入超时 10s</span>
                .readTimeout(<span class="hljs-number">30</span>, TimeUnit.SECONDS) <span class="hljs-comment">// 读取超时  30s</span>
                .build();
</code>

该方式是设置全局的超时规则,同样我们也可以对于不同的情况设置不同的超时选项。

关键点: 通过OkHttpClient.newBuilder()构造一个client的复制体。注意,此复制类似于从client 扣下来一块设置不同的规则,同时复制体上设置的规则不会影响原client

<code>
    <span class="hljs-javadoc">/**
     * 设置不同的超时时间
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">singleTimeOut</span>() <span class="hljs-keyword">throws</span> Exception {

        OkHttpClient client = <span class="hljs-keyword">new</span> OkHttpClient();

        Request request = <span class="hljs-keyword">new</span> Request.Builder().url(<span class="hljs-string">"http://xxxx"</span>).build();

        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// 从client 中抠出一小块,用于单独的设置</span>
            <span class="hljs-comment">// 设置读取时间为1ms,肯定会超时</span>
            OkHttpClient copy = client.newBuilder()
                    .readTimeout(<span class="hljs-number">1</span>, TimeUnit.MILLISECONDS).build();

            Response response = copy.newCall(request).execute();
            System.out.println(<span class="hljs-string">"Response 1 succeeded: "</span> + response);
        } <span class="hljs-keyword">catch</span> (IOException e) {
            System.out.println(<span class="hljs-string">"Response 1 failed: "</span> + e);
        }

        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// 从client 中抠出一小块,用于单独的设置</span>
            <span class="hljs-comment">// 设置读取时间为3000ms</span>
            OkHttpClient copy = client.newBuilder()
                    .readTimeout(<span class="hljs-number">3000</span>, TimeUnit.MILLISECONDS).build();

            Response response = copy.newCall(request).execute();
            System.out.println(<span class="hljs-string">"Response 2 succeeded: "</span> + response);
        } <span class="hljs-keyword">catch</span> (IOException e) {
            System.out.println(<span class="hljs-string">"Response 2 failed: "</span> + e);
        }

    }
</code>

结果

<code>Response <span class="hljs-number">1</span> failed: java.net.SocketTimeoutException: Read timed out

Response <span class="hljs-number">2</span> succeeded: Response{protocol=http/<span class="hljs-number">1.1</span>, code=<span class="hljs-number">200</span>, message=OK, url=http:<span class="hljs-comment">//xxx/}</span>
</code>

Interceptor(拦截器)

Interceptor:监听网络的请求与变化。

实现方式:

  • 自定义类实现Interceptor接口
  • OkHttpClient.Builder().addInterceptor(自定义Interceptor).build();

作用:

  • 打印网络请求的请求和响应的信息。
  • 对网络请求做统一处理,列入添加共同的头信息。

打印网络请求的信息

为了方便,使用Eclipse进行编写。

<code><span class="hljs-keyword">static</span> class LoggingInterceptor implements Interceptor {

        <span class="hljs-keyword">public</span> Response <span class="hljs-title">intercept</span>(Chain chain) <span class="hljs-keyword">throws</span> IOException {
            <span class="hljs-comment">// 固定写法</span>
            Request request = chain.request();
            <span class="hljs-comment">// 固定写法</span>
            Response response = chain.proceed(request);

            <span class="hljs-comment">// --------Log 信息---在android 中改成Log.i()即可</span>
            System.out.println(<span class="hljs-string">"=====请求========"</span>);

            <span class="hljs-comment">// 请求行</span>
            System.out.println(request);
            <span class="hljs-comment">// 请求头信息</span>
            System.out.println(request.headers());

            <span class="hljs-comment">// form 表单信息。注意,此时只对form 表单适用</span>
            FormBody form = (FormBody) request.body();
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; form.size(); i++) {
                System.out.println(form.encodedName(i) + <span class="hljs-string">"="</span>
                        + form.encodedValue(i));
            }

            System.out.println(<span class="hljs-string">"=====响应========"</span>);
            <span class="hljs-comment">// 响应行</span>
            System.out.println(response);
            <span class="hljs-comment">// 响应头</span>
            System.out.println(response.headers());
            <span class="hljs-comment">// 响应体</span>
            System.out.println(response.body().string());
            <span class="hljs-keyword">return</span> response;
        }

    }

</code>

添加Interceptor

<code>        <span class="hljs-keyword">final</span> OkHttpClient client = <span class="hljs-keyword">new</span> OkHttpClient.Builder()
                <span class="hljs-comment">// 添加Interceptor</span>
                .addNetworkInterceptor(<span class="hljs-keyword">new</span> LoggingInterceptor()).build();
        Request request = <span class="hljs-keyword">new</span> Request.Builder().url(BAIDU_MP3_PATH)
                .post(<span class="hljs-keyword">new</span> FormBody.Builder().add(<span class="hljs-string">"name"</span>, <span class="hljs-string">"123"</span>).build())
                .build();
        ....
</code>

注意,使用的POST 表单提交。

看一下结果

<code>=====请求========
Request{method=POST, url=http:<span class="hljs-comment">//tingapi.ting.baidu.com/v1/restserver/ting?from=qianqian&amp;version=2.1.0&amp;method=baidu.ting.billboard.billList&amp;format=json&amp;type=1&amp;offset=0&amp;size=1, tag=Request{method=POST, url=http://tingapi.ting.baidu.com/v1/restserver/ting?from=qianqian&amp;version=2.1.0&amp;method=baidu.ting.billboard.billList&amp;format=json&amp;type=1&amp;offset=0&amp;size=1, tag=null}}</span>
Content-Type: application/x-www-form-urlencoded
Content-Length: <span class="hljs-number">8</span>
Host: tingapi.ting.baidu.com
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/<span class="hljs-number">3.3</span><span class="hljs-number">.1</span>

name=<span class="hljs-number">123</span>
=====响应========
Response{protocol=http/<span class="hljs-number">1.1</span>, code=<span class="hljs-number">200</span>, message=OK, url=http:<span class="hljs-comment">//tingapi.ting.baidu.com/v1/restserver/ting?from=qianqian&amp;version=2.1.0&amp;method=baidu.ting.billboard.billList&amp;format=json&amp;type=1&amp;offset=0&amp;size=1}</span>
Date: Wed, <span class="hljs-number">29</span> Jun <span class="hljs-number">2016</span> <span class="hljs-number">08</span>:<span class="hljs-number">22</span>:<span class="hljs-number">28</span> GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
X-LIGHTTPD-LOGID: <span class="hljs-number">624562847</span>
RT: <span class="hljs-number">624562847</span>_d41d8cd98f00b204e9800998ecf8427e_d41d8cd98f00b204e9800998ecf8427e
Server: Apache1<span class="hljs-number">.0</span>
SC: MISS<span class="hljs-number">.0</span><span class="hljs-number">.0</span>
tracecode: <span class="hljs-number">13487736192701306048062916</span>
Set-Cookie: BAIDUID=<span class="hljs-number">8184</span>D1B9CC4C45EB85C3AB62E8441688:FG=<span class="hljs-number">1</span>; expires=Thu, <span class="hljs-number">29</span>-Jun-<span class="hljs-number">17</span> <span class="hljs-number">08</span>:<span class="hljs-number">22</span>:<span class="hljs-number">28</span> GMT; max-age=<span class="hljs-number">31536000</span>; path=/; domain=.baidu.com; version=<span class="hljs-number">1</span>
P3P: CP=<span class="hljs-string">" OTI DSP COR IVA OUR IND COM "</span>

{<span class="hljs-string">"song_list"</span>:[{<span class="hljs-string">"artist_id"</span>:<span class="hljs-string">"247010469"</span>,<span class="hljs-string">"language"</span>:<span class="hljs-string">"/u56fd/u8bed"</span>,<span class="hljs-string">"pic_big"</span>:<span class="hljs-string">"http:////musicdata.baidu.com//data2//pic//0c87cb20fd6b543cd1c90d1d82f1f6c5//266782982//266782982.jpg"</span>,<span class="hljs-string">"pic_small"</span>:<span class="hljs-string">"http:////musicdata.baidu.com//data2//pic//169cbb0d82522568dea198363f7028ec//266782985//266782985.jpg"</span>,<span class="hljs-string">"country"</span>:<span class="hljs-string">"/u5185/u5730"</span>,<span class="hljs-string">"area"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"publishtime"</span>:<span class="hljs-string">"2016-06-20"</span>,<span class="hljs-string">"album_no"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"lrclink"</span>:<span class="hljs-string">"http:////musicdata.baidu.com//data2//lrc//1f75306d7f1e25f723d3d175f5ef3d46//266791084//266791084.lrc"</span>,<span class="hljs-string">"copy_type"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"hot"</span>:<span class="hljs-string">"276927"</span>,<span class="hljs-string">"all_artist_ting_uid"</span>:<span class="hljs-string">"232954914"</span>,<span class="hljs-string">"resource_type"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"is_new"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"rank_change"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"rank"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"all_artist_id"</span>:<span class="hljs-string">"247010469"</span>,<span class="hljs-string">"style"</span>:<span class="hljs-string">"/u6d41/u884c"</span>,<span class="hljs-string">"del_status"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"relate_status"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"toneid"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"all_rate"</span>:<span class="hljs-string">"64,128,256"</span>,<span class="hljs-string">"sound_effect"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"file_duration"</span>:<span class="hljs-number">0</span>,<span class="hljs-string">"has_mv_mobile"</span>:<span class="hljs-number">1</span>,<span class="hljs-string">"versions"</span>:<span class="hljs-string">""</span>,<span class="hljs-string">"bitrate_fee"</span>:<span class="hljs-string">"{/"0/":/"0|0/",/"1/":/"-1|-1/"}"</span>,<span class="hljs-string">"song_id"</span>:<span class="hljs-string">"266784254"</span>,<span class="hljs-string">"title"</span>:<span class="hljs-string">"/u7ea2"</span>,<span class="hljs-string">"ting_uid"</span>:<span class="hljs-string">"232954914"</span>,<span class="hljs-string">"author"</span>:<span class="hljs-string">"/u51af/u5efa/u5b87"</span>,<span class="hljs-string">"album_id"</span>:<span class="hljs-string">"266784256"</span>,<span class="hljs-string">"album_title"</span>:<span class="hljs-string">"/u7ea2"</span>,<span class="hljs-string">"is_first_publish"</span>:<span class="hljs-number">0</span>,<span class="hljs-string">"havehigh"</span>:<span class="hljs-number">0</span>,<span class="hljs-string">"charge"</span>:<span class="hljs-number">0</span>,<span class="hljs-string">"has_mv"</span>:<span class="hljs-number">1</span>,<span class="hljs-string">"learn"</span>:<span class="hljs-number">0</span>,<span class="hljs-string">"song_source"</span>:<span class="hljs-string">"web"</span>,<span class="hljs-string">"piao_id"</span>:<span class="hljs-string">"0"</span>,<span class="hljs-string">"korean_bb_song"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"resource_type_ext"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"mv_provider"</span>:<span class="hljs-string">"0100000000"</span>,<span class="hljs-string">"artist_name"</span>:<span class="hljs-string">"/u51af/u5efa/u5b87"</span>}],<span class="hljs-string">"billboard"</span>:{<span class="hljs-string">"billboard_type"</span>:<span class="hljs-string">"1"</span>,<span class="hljs-string">"billboard_no"</span>:<span class="hljs-string">"1871"</span>,<span class="hljs-string">"update_date"</span>:<span class="hljs-string">"2016-06-28"</span>,<span class="hljs-string">"billboard_songnum"</span>:<span class="hljs-string">"190"</span>,<span class="hljs-string">"havemore"</span>:<span class="hljs-number">1</span>,<span class="hljs-string">"name"</span>:<span class="hljs-string">"/u65b0/u6b4c/u699c"</span>,<span class="hljs-string">"comment"</span>:<span class="hljs-string">"/u8be5/u699c/u5355/u662f/u6839/u636e/u767e/u5ea6/u97f3/u4e50/u5e73/u53f0/u6b4c/u66f2/u6bcf/u65e5/u64ad/u653e/u91cf/u81ea/u52a8/u751f/u6210/u7684/u6570/u636e/u699c/u5355/uff0c/u7edf/u8ba1/u8303/u56f4/u4e3a/u8fd1/u671f/u53d1/u884c/u7684/u6b4c/u66f2/uff0c/u6bcf/u65e5/u66f4/u65b0/u4e00/u6b21"</span>,<span class="hljs-string">"pic_s640"</span>:<span class="hljs-string">"http:////c.hiphotos.baidu.com//ting//pic//item//f7246b600c33874495c4d089530fd9f9d62aa0c6.jpg"</span>,<span class="hljs-string">"pic_s444"</span>:<span class="hljs-string">"http:////d.hiphotos.baidu.com//ting//pic//item//78310a55b319ebc4845c84eb8026cffc1e17169f.jpg"</span>,<span class="hljs-string">"pic_s260"</span>:<span class="hljs-string">"http:////b.hiphotos.baidu.com//ting//pic//item//e850352ac65c1038cb0f3cb0b0119313b07e894b.jpg"</span>,<span class="hljs-string">"pic_s210"</span>:<span class="hljs-string">"http:////business.cdn.qianqian.com//qianqian//pic//bos_client_c49310115801d43d42a98fdc357f6057.jpg"</span>,<span class="hljs-string">"web_url"</span>:<span class="hljs-string">"http:////music.baidu.com//top//new"</span>},<span class="hljs-string">"error_code"</span>:<span class="hljs-number">22000</span>}

</code>

打印成功。。。。

当然,该实现比较粗糙,仅作原理的解释

通过上面的实现,可以看到在LoggingInterceptor中可以获取到request对象,那么我们可以通过设置其header(),实现全局的request头部信息的修改。

该博客中的源码已经共享到https://github.com/AlexSmille/alex_mahao_sample/tree/master/networklib,有需要请移步。

未经允许不得转载:冰点网络 » okhttp使用总结

分享到:更多 ()

评论 抢沙发

评论前必须登录!