好用的网络请求库Retrofit2(入门及讲解)
PeaDeBeuzev
9年前
<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>