好用的网络请求库Retrofit2(入门及讲解)

PeaDeBeuzev 8年前
   <h2>前言</h2>    <p>首先,先给出官网:</p>    <p><a href="/misc/goto?guid=4958964956869128717">GitHub-Retrofit</a><br> <a href="/misc/goto?guid=4958837204152834453">官网-Retrofit</a></p>    <p>其次,要吐槽一下官网首页给出的例子。如果你照着例子改,会发现根本没法运行,不是少包就是少关键语句。</p>    <p>相关内容可以参看我的另一篇文章:<a href="/misc/goto?guid=4959671479809150877">Retrofit(2.0)入门小错误 – Could not locate ResponseBody xxx Tried: * retrofit.BuiltInConverters</a></p>    <h2>小栗子(example)</h2>    <p>无论如何咱们还是先跑起来一个小栗子吧。</p>    <p>首先,在gralde文件中引入后续要用到的库。</p>    <pre>  <code class="language-java">compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'  compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'  compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'    compile 'com.squareup.okhttp:okhttp:2.4.0'    compile 'io.reactivex:rxjava:1.0.14'  compile 'io.reactivex:rxandroid:1.0.1'</code></pre>    <p>接下来跟着我一步两步往下走。</p>    <p>创建服务类和Bean</p>    <pre>  <code class="language-java"> public static class Contributor {     public final String login;      public final int contributions;      public Contributor(String login, int contributions) {          this.login = login;          this.contributions = contributions;      }      @Override      public String toString() {          return "Contributor{" +                  "login='" + login + '\'' +                  ", contributions=" + contributions +                  '}';      }  }  public interface GitHub {      @GET("/repos/{owner}/{repo}/contributors")      Call<List<Contributor>> contributors(              @Path("owner") String owner,              @Path("repo") String repo);  }</code></pre>    <p>接下来创建Retrofit2的实例,并设置BaseUrl和Gson转换。</p>    <pre>  <code class="language-java">Retrofit retrofit = new Retrofit.Builder()          .baseUrl("https://api.github.com")          .addConverterFactory(GsonConverterFactory.create())          .client(new OkHttpClient())          .build();</code></pre>    <p>创建请求服务,并为网络请求方法设置参数</p>    <pre>  <code class="language-java">GitHub gitHubService = retrofit.create(GitHub.class);  Call<List<Contributor>> call = gitHubService.contributors("square", "retrofit");</code></pre>    <p>最后,请求网络,并获取响应</p>    <pre>  <code class="language-java">try{      Response<List<Contributor>> response = call.execute(); // 同步      Log.d(TAG, "response:" + response.body().toString());  } catch (IOException e) {      e.printStackTrace();  }</code></pre>    <h2>Call是Retrofit中重要的一个概念,代表被封装成单个请求/响应的交互行为</h2>    <p>通过调用Retrofit2的execute(同步)或者enqueue(异步)方法,发送请求到网络服务器,并返回一个响应(Response)。</p>    <ol>     <li>独立的请求和响应模块</li>     <li>从响应处理分离出请求创建</li>     <li>每个实例只能使用一次。</li>     <li>Call可以被克隆。</li>     <li>支持同步和异步方法。</li>     <li>能够被取消。</li>    </ol>    <p>由于call只能被执行一次,所以按照上面的顺序执行会得到如下错误。</p>    <pre>  <code class="language-java">java.lang.IllegalStateException: Already executed</code></pre>    <p>我们可以通过clone,来克隆一份call,从新调用。</p>    <pre>  <code class="language-java">// clone  Call<List<Contributor>> call1 = call.clone();  // 5. 请求网络,异步  call1.enqueue(new Callback<List<Contributor>>() {      @Override      public void onResponse(Response<List<Contributor>> response, Retrofit retrofit) {          Log.d(TAG, "response:" + response.body().toString());      }        @Override      public void onFailure(Throwable t) {        }  });</code></pre>    <h2>参数相关</h2>    <p>网络访问肯定要涉及到参数请求,Retrofit为我们提供了各式各样的组合方法。下面以标题+小例子的方式给出讲解。</p>    <p>固定查询参数</p>    <pre>  <code class="language-java">// 服务  interface SomeService {   @GET("/some/endpoint?fixed=query")   Call<SomeResponse> someEndpoint();  }    // 方法调用  someService.someEndpoint();    // 请求头  // GET /some/endpoint?fixed=query HTTP/1.1</code></pre>    <p>动态参数</p>    <pre>  <code class="language-java">// 服务  interface SomeService {   @GET("/some/endpoint")   Call<SomeResponse> someEndpoint(   @Query("dynamic") String dynamic);  }    // 方法调用  someService.someEndpoint("query");    // 请求头  // GET /some/endpoint?dynamic=query HTTP/1.1</code></pre>    <h3>动态参数(Map)</h3>    <pre>  <code class="language-java">// 服务  interface SomeService {   @GET("/some/endpoint")   Call<SomeResponse> someEndpoint(   @QueryMap Map<String, String> dynamic);  }    // 方法调用  someService.someEndpoint(   Collections.singletonMap("dynamic", "query"));  // 请求头  // GET /some/endpoint?dynamic=query HTTP/1.1</code></pre>    <p>省略动态参数</p>    <pre>  <code class="language-java">interface SomeService {   @GET("/some/endpoint")   Call<SomeResponse> someEndpoint(   @Query("dynamic") String dynamic);  }    // 方法调用  someService.someEndpoint(null);    // 请求头  // GET /some/endpoint HTTP/1.1</code></pre>    <p>固定+动态参数</p>    <pre>  <code class="language-java">interface SomeService {   @GET("/some/endpoint?fixed=query")   Call<SomeResponse> someEndpoint(   @Query("dynamic") String dynamic);  }    // 方法调用  someService.someEndpoint("query");    // 请求头  // GET /some/endpoint?fixed=query&dynamic=query HTTP/1.1</code></pre>    <p>路径替换</p>    <pre>  <code class="language-java">interface SomeService {   @GET("/some/endpoint/{thing}")   Call<SomeResponse> someEndpoint(   @Path("thing") String thing);  }    someService.someEndpoint("bar");    // GET /some/endpoint/bar HTTP/1.1</code></pre>    <p>固定头</p>    <pre>  <code class="language-java">interface SomeService {   @GET("/some/endpoint")   @Headers("Accept-Encoding: application/json")   Call<SomeResponse> someEndpoint();  }    someService.someEndpoint();    // GET /some/endpoint HTTP/1.1  // Accept-Encoding: application/json</code></pre>    <p>动态头</p>    <pre>  <code class="language-java">interface SomeService {   @GET("/some/endpoint")   Call<SomeResponse> someEndpoint(   @Header("Location") String location);  }    someService.someEndpoint("Droidcon NYC 2015");    // GET /some/endpoint HTTP/1.1  // Location: Droidcon NYC 2015</code></pre>    <p>固定+动态头</p>    <pre>  <code class="language-java">interface SomeService {   @GET("/some/endpoint")   @Headers("Accept-Encoding: application/json")   Call<SomeResponse> someEndpoint(   @Header("Location") String location);  }    someService.someEndpoint("Droidcon NYC 2015");    // GET /some/endpoint HTTP/1.1  // Accept-Encoding: application/json  // Location: Droidcon NYC 2015</code></pre>    <p>Post请求,无Body</p>    <pre>  <code class="language-java">interface SomeService {   @POST("/some/endpoint")   Call<SomeResponse> someEndpoint();  }    someService.someEndpoint();    // POST /some/endpoint?fixed=query HTTP/1.1  // Content-Length: 0</code></pre>    <p>Post请求有Body</p>    <pre>  <code class="language-java">interface SomeService {   @POST("/some/endpoint")   Call<SomeResponse> someEndpoint(   @Body SomeRequest body);  }    someService.someEndpoint();    // POST /some/endpoint HTTP/1.1  // Content-Length: 3  // Content-Type: greeting  //  // Hi!</code></pre>    <p>表单编码字段</p>    <pre>  <code class="language-java">interface SomeService {   @FormUrlEncoded   @POST("/some/endpoint")   Call<SomeResponse> someEndpoint(   @Field("name1") String name1,   @Field("name2") String name2);  }    someService.someEndpoint("value1", "value2");    // POST /some/endpoint HTTP/1.1  // Content-Length: 25  // Content-Type: application/x-www-form-urlencoded  //  // name1=value1&name2=value2</code></pre>    <p>表单编码字段(Map)</p>    <pre>  <code class="language-java">interface SomeService {   @FormUrlEncoded   @POST("/some/endpoint")   Call<SomeResponse> someEndpoint(   @FieldMap Map<String, String> names);  }    someService.someEndpoint(   // ImmutableMap是OKHttp中的工具类   ImmutableMap.of("name1", "value1", "name2", "value2"));    // POST /some/endpoint HTTP/1.1  // Content-Length: 25  // Content-Type: application/x-www-form-urlencoded  //  // name1=value1&name2=value2</code></pre>    <h2>动态Url(Dynamic URL parameter)</h2>    <pre>  <code class="language-java">interface GitHubService {   @GET("/repos/{owner}/{repo}/contributors")   Call<List<Contributor>> repoContributors(   @Path("owner") String owner,   @Path("repo") String repo);     @GET   Call<List<Contributor>> repoContributorsPaginate(   @Url String url);  }    // 调用  Call<List<Contributor>> call = gitHubService.repoContributors("square", "retrofit");  Response<List<Contributor>> response = call.execute();  // 响应结果  // HTTP/1.1 200 OK  // Link: <https://api.github.com/repositories/892275/contributors?  page=2>; rel="next", <https://api.github.com/repositories/892275/  contributors?page=3>; rel="last"    // 获取到头中的数据  String links = response.headers().get("Link");  String nextLink = nextFromGitHubLinks(links);  // https://api.github.com/repositories/892275/contributors?page=2</code></pre>    <h3>可插拔的执行机制(Multiple, pluggable execution mechanisms)</h3>    <pre>  <code class="language-java">interface GitHubService {   @GET("/repos/{owner}/{repo}/contributors")   // Call 代表的是CallBack回调机制   Call<List<Contributor>> repoContributors(   @Path("owner") String owner,   @Path("repo") String repo);     @GET("/repos/{owner}/{repo}/contributors")   // Observable 代表的是RxJava的执行   Observable<List<Contributor>> repoContributors2(   @Path("owner") String owner,   @Path("repo") String repo);     @GET("/repos/{owner}/{repo}/contributors")   Future<List<Contributor>> repoContributors3(   @Path("owner") String owner,   @Path("repo") String repo);  }</code></pre>    <p>注意,要在构建Retrofit时指定适配器模式为RxJavaCallAdapterFactory</p>    <pre>  <code class="language-java">Retrofit retrofit = new Retrofit.Builder()      .addConverterFactory(GsonConverterFactory.create())      .addCallAdapterFactory(RxJavaCallAdapterFactory.create())      .baseUrl("http://www.duitang.com")      .build();</code></pre>    <p>否则,会报出如下错误:</p>    <pre>  <code class="language-java">Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for rx.Observable<com.bzh.sampleretrofit.ClubBean>. Tried:  * retrofit.ExecutorCallAdapterFactory</code></pre>    <h2>Retrofit执行模式</h2>    <p><img alt="" src="https://simg.open-open.com/show/36ab694629979f514881eeaadb5837f8.png"></p>    <p><img alt="" src="https://simg.open-open.com/show/8342bff07681e6498e6523d09d02e659.png"></p>    <p><img alt="" src="https://simg.open-open.com/show/e7af4f3db91d5c510c71dc4deb59aaf3.png"></p>    <p><img alt="" src="https://simg.open-open.com/show/5ea54753466a0649a9b1299a11182366.png"></p>    <h2>最后</h2>    <p>授人以鱼不如授人以渔 <a href="/misc/goto?guid=4959671479891269059">这是出自官方开发人员的讲解的网站(自备梯子)</a></p>    <p>Retrofit作为一个上层框架,自然有很多底层lib库支持,<code>okio</code>和<code>okhttp</code>都包含其中。<br> <img alt="" src="https://simg.open-open.com/show/c5b03dc92fb3c1002f660f906c6f50b3.png"></p>    <p><a href="/misc/goto?guid=4959671479993942577">这是一些关于<code>OK</code>库的讲解</a></p>    <p>来自: <a href="/misc/goto?guid=4959671480072461491" rel="nofollow">http://blog.csdn.net/biezhihua/article/details/49232289</a></p>