1. 程式人生 > >好用的網路請求庫Retrofit2(入門及講解)

好用的網路請求庫Retrofit2(入門及講解)

前言

首先,先給出官網:

其次,要吐槽一下官網首頁給出的例子。如果你照著例子改,會發現根本沒法執行,不是少包就是少關鍵語句。

小栗子(example)

無論如何咱們還是先跑起來一個小栗子吧。

首先,在gralde檔案中引入後續要用到的庫。


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'

接下來跟著我一步兩步往下走。

建立服務類和Bean

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

接下來建立Retrofit2的例項,並設定BaseUrl和Gson轉換。

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com")
        .addConverterFactory(GsonConverterFactory.create())
        .client(new OkHttpClient())
        .build();

建立請求服務,併為網路請求方法設定引數

GitHub gitHubService = retrofit.create(GitHub.class);
Call<List<Contributor>> call = gitHubService.contributors("square", "retrofit");

最後,請求網路,並獲取響應

try{
    Response<List<Contributor>> response = call.execute(); // 同步
    Log.d(TAG, "response:" + response.body().toString());
} catch (IOException e) {
    e.printStackTrace();
}

Call是Retrofit中重要的一個概念,代表被封裝成單個請求/響應的互動行為

通過呼叫Retrofit2的execute(同步)或者enqueue(非同步)方法,傳送請求到網路伺服器,並返回一個響應(Response)。

  1. 獨立的請求和響應模組
  2. 從響應處理分離出請求建立
  3. 每個例項只能使用一次。
  4. Call可以被克隆。
  5. 支援同步和非同步方法。
  6. 能夠被取消。

由於call只能被執行一次,所以按照上面的順序執行會得到如下錯誤。

java.lang.IllegalStateException: Already executed

我們可以通過clone,來克隆一份call,從新呼叫。

// 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) {

    }
});

引數相關

網路訪問肯定要涉及到引數請求,Retrofit為我們提供了各式各樣的組合方法。下面以標題+小例子的方式給出講解。

固定查詢引數

// 服務
interface SomeService {
 @GET("/some/endpoint?fixed=query")
 Call<SomeResponse> someEndpoint();
}

// 方法呼叫
someService.someEndpoint();

// 請求頭
// GET /some/endpoint?fixed=query HTTP/1.1

動態引數

// 服務
interface SomeService {
 @GET("/some/endpoint")
 Call<SomeResponse> someEndpoint(
 @Query("dynamic") String dynamic);
}

// 方法呼叫
someService.someEndpoint("query");

// 請求頭
// GET /some/endpoint?dynamic=query HTTP/1.1

動態引數(Map)

// 服務
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

省略動態引數

interface SomeService {
 @GET("/some/endpoint")
 Call<SomeResponse> someEndpoint(
 @Query("dynamic") String dynamic);
}

// 方法呼叫
someService.someEndpoint(null);

// 請求頭
// GET /some/endpoint HTTP/1.1

固定+動態引數

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

路徑替換

interface SomeService {
 @GET("/some/endpoint/{thing}")
 Call<SomeResponse> someEndpoint(
 @Path("thing") String thing);
}

someService.someEndpoint("bar");

// GET /some/endpoint/bar HTTP/1.1

固定頭

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

動態頭

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

固定+動態頭

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

Post請求,無Body

interface SomeService {
 @POST("/some/endpoint")
 Call<SomeResponse> someEndpoint();
}

someService.someEndpoint();

// POST /some/endpoint?fixed=query HTTP/1.1
// Content-Length: 0

Post請求有Body

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!

表單編碼欄位

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

表單編碼欄位(Map)

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

動態Url(Dynamic URL parameter)

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

可插拔的執行機制(Multiple, pluggable execution mechanisms)

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

注意,要在構建Retrofit時指定介面卡模式為RxJavaCallAdapterFactory

Retrofit retrofit = new Retrofit.Builder()
    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .baseUrl("http://www.duitang.com")
    .build();

否則,會報出如下錯誤:

Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for rx.Observable<com.bzh.sampleretrofit.ClubBean>. Tried:
* retrofit.ExecutorCallAdapterFactory

Retrofit執行模式

最後

Retrofit作為一個上層框架,自然有很多底層lib庫支援,okiookhttp都包含其中。