Retrofit2.0介紹使用封裝

前言

時至今日,Android的網絡框架不再像之前那麼到處都是,隨着Google把 HttpClient直接刪掉,似乎意味着Android越來越成熟。網絡框架中的佼佼者Volley也不再那麼光鮮,取而代之的是 Retrofit 和 okHttp。 
感覺很像 OnePiece 中白鬍子的離去象徵着時代的變革,新時代的開始,多弗的垮臺象徵着七武海制度的取締一樣,不會使用Retrofit + okHttp + RxJava等一系列技術,就邁不進新時代的門檻,也不足以稱爲一個合格的開發者。

哈哈閒話不多說了,只是在 Android這個平臺上 開發這麼長時間的一點感觸。各位看看笑笑也就算了。還是來介紹 Retrofit 吧。(直接略過1.9)

本文的所有代碼在 retrofitLearn;

1. Retrofit介紹

A type-safe HTTP client for Android and Java 
一個用於Android和Java平臺的類型安全的網絡框架

Retrofit is a type-safe REST client for Android built by Square. The library provides a powerful framework for authenticating and interacting with APIs and sending network requests with OkHttp. 
Retrofit 是一個Square開發的類型安全的REST安卓客戶端請求庫。這個庫爲網絡認證、API請求以及用OkHttp發送網絡請求提供了強大的框架 。

Retrofit 把REST API返回的數據轉化爲Java對象,就像ORM框架那樣,把數據庫內的存儲的數據轉化爲相應的Java bean對象。

那麼我們知道Retrofit是一個類型安全的網絡框架,而且它是使用REST API的,接下來我們看看什麼是REST吧。

2. REST 介紹:

Resources Representational State Transfer 
資源表現層狀態轉化

  • 每一個URI代表一種資源
  • 客戶端和服務器之間,傳遞這種資源的某種 表現層(“資源”具體呈現出來的形式,比如.txt,.png,.jpg)
  • 客戶端通過四個HTTP動詞(GET用來獲取資源,POST用來新建或更新資源,PUT用來更新資源,DELETE用來刪除資源)對服務器端資源進行操作,實現”表現層狀態轉化”

關於REST,這裏只僅僅列出了結論,文末有超級好的鏈接,請查看。如果你所使用的API是REST的,那麼恭喜你,這樣的API看起來真的很舒服,慶幸的是我司就使用的是REST API。

知道了REST是什麼,那接下啦就開始介紹Retrofit的用法啦。

3. Retrofit基本用法(未封裝)

1.在build.gradle中添加依賴

//okHttp
compile 'com.squareup.okhttp3:okhttp:3.2.0'

//retrofit
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.創建接口,聲明API

//Retrofit turns your HTTP API into a Java interface.
//創建接口,聲明GitHub的API
public interface GitHubAPI {

     /*
       請求該接口:https://api.github.com/users/baiiu
     */
     @GET("users/{user}") 
     Call<User> userInfo(@Path("user") String user);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.在MainActivity.onCreate()方法中調用

/*
1.初始化OkHttpClient
*/
OkHttpClient client = new OkHttpClient();

/*
2.創建Retrofit
*/
retrofit = new Retrofit.Builder()
        //設置OKHttpClient
        .client(client)
        //設置baseUrl,注意,baseUrl必須後綴"/"
        .baseUrl("https://api.github.com/")

        //添加Gson轉換器
        .addConverterFactory(GsonConverterFactory.create())
        .build();

/*
2.獲取GitHub的API
*/
GitHubAPI gitHubAPI = retrofit.create(GitHubAPI.class);

/*
3.異步調用
*/
Call<User> userCall = gitHubAPI.userInfo("baiiu");
userCall.enqueue(new Callback<User>() {
      @Override public void onResponse(Call<User> call, Response<User> response) {
        User body = response.body();
        LogUtil.d(body == null ? "body == null" : body.toString());
      }

      @Override public void onFailure(Call<User> call, Throwable t) {
        /*
         判斷是否被cancel掉了
        */
        if (call.isCanceled()) {
          LogUtil.d("the call is canceled , " + toString());
        } else {
          LogUtil.e(t.toString());
        }
      }
    });

/*
取消調用
*/
//userCall.cancel();

/*
同步調用,舉個例子,寫法如下(當然不能在主線程調用):
*/


//Each instance can only be used once, but calling clone() will create a new instance that can be used.
Call<User> clone = userCall.clone();

Response<User> response = clone.execute();
User body = response.body();
LogUtil.d(body == null ? "body == null" : body.toString());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

注意: 
    1. 設置BaseUrl時必須後綴”/”,如 https://api.github.com/,這篇文章說的超清晰:Retrofit 2.0: The biggest update yet on the best HTTP Client Library for Android

     2. 因爲Retrofit在創建時候傳入了BaseUrl,所以基本上所有請求都基於該BaseUrl了。但是總有些API不是以該BaseUrl開頭的,特別是有些公司可能不是restful API的。那麼該怎麼辦呢。Retrofit提供了一個註解@Url解決這個問題,可以在運行時直接使用該Url直接訪問。代碼如下:

//使用@Url註解,傳入該Url地址就OK啦,跨過BaseUrl,直接訪問該Url地址
@GET Call<Daily> getNewsList(@Url String url);
  • 1
  • 2
  • 1
  • 2

恩,接下來,知道了Retrofit的基本使用,接下來就是介紹Retrofit的註解了。

4. Retrofit註解

Retrofit使用註解來聲明API的請求方式、請求參數等。這裏只介紹一下它的 @Header 註解,其他的請閱讀 官網文檔 和 鴻洋的 Retrofit2 完全解析 探索與okhttp之間的關係。(這塊寫了也是翻譯官方文檔的和參考鴻樣的,會拉長篇幅)

請求頭的設置可以通過 @Header 註解添加,又有兩種添加方式:

  • 設置靜態的請求頭。
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();

@Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 動態的設置請求頭。
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
  • 1
  • 2
  • 1
  • 2

Note that headers do not overwrite each other. All headers with the same name will be included in the request. 
同一個請求的同一個請求頭在不同地方的設置不會被覆蓋,而是會被全部添加進請求頭中。

Headers that need to be added to every request can be specified using an OkHttp interceptor. 
如果要給每個請求都添加同樣的Header時,可以使用okHttp的 Interceptor 。

那麼,接下來就介紹Interceptor。

5.Interceptors使用

Retrofit 2.0 底層強制依賴okHttp,所以可以使用okHttp的攔截器Interceptors 來對所有請求進行再處理。

Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls.

使用時,實現 Interceptor 接口,做我們自己的處理。 
目前使用中,一般用來設置UA設置緩存策略 、打印Log 等。這裏介紹一個設置UA的Interceptor:

1.創建設置UA的Interceptor

public final class UserAgentInterceptor implements Interceptor {
  private static final String USER_AGENT_HEADER_NAME = "User-Agent";
  private final String userAgentHeaderValue;

  public UserAgentInterceptor(String userAgentHeaderValue) {
    this.userAgentHeaderValue = userAgentHeaderValue;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    final Request originalRequest = chain.request();

    final Request requestWithUserAgent = originalRequest.newBuilder()

        //移除先前默認的UA
        .removeHeader(USER_AGENT_HEADER_NAME)

        //設置UA
        .addHeader(USER_AGENT_HEADER_NAME, userAgentHeaderValue)


        .build();
    return chain.proceed(requestWithUserAgent);
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

2.創建okHttpClient時 設置該Interceptor

okHttpClient = new OkHttpClient.Builder()
        //添加UA
        .addInterceptor(new UserAgentInterceptor(HttpHelper.getUserAgent()))

        //失敗重連
        .retryOnConnectionFailure(true)

        //time out
        .readTimeout(TIMEOUT_READ, TimeUnit.SECONDS)
        .connectTimeout(TIMEOUT_CONNECTION, TimeUnit.SECONDS)

        .build();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Interceptors 和 Header 的配合使用讓我們很方便的使用Http本身的緩存。之前用Volley的時候這塊就比較難處理。解下來就說說Retrofit的緩存機制。

6. 配置網絡緩存

使用Retrofit 感覺最方便的就是它提供的緩存方式(雖然這是Http本身的),我們可以隨便通過Header和Interceptor配置自己的緩存策略。

6.1 緩存設置原理:

緩存的設置是通過設置 Http請求頭和響應頭中的 Cache-Control 的 max-age 屬性達到的。在複寫 Interceptor 時,通過設置響應頭的 Cache-Control來達到目的。關於Http的本身的緩存命中請查看文末鏈接 (畢竟Http也是個大傢伙) 。

6.2 緩存設置步驟

  1. 設置緩存目錄 
    retrofit本身默認無緩存,連緩存目錄都沒有提供默認的,所以 要想實現緩存,必須要在創建OkHttpClient時設置緩存目錄。 這塊超級坑,自己試了半天,最終看了下Cache那邊的源碼,竟然沒有默認的緩存目錄!!!

  2. 設置緩存的方式

    • 通過添加 @Headers("Cache-Control: max-age=120") 進行設置。添加了Cache-Control 的請求,retrofit 會默認緩存該請求的返回數據。
    • 通過Interceptors實現緩存。

6.3 實現Interceptor進行緩存

提供了兩種緩存策略(參考),設置緩存時Application Interceptor 和 Net Interceptor 都要設置:

  • AllCachedInterceptor 
    有網沒網都先走緩存,可以設置間歇時間。 
    同時可以針對某一個request設置單獨的緩存時間,使用 @Headers("Cache-Control: max-age=120") 就行。

  • OnOffLineCachedInterceptor 
    有網可以不走緩存,可設定時間,設置爲0意味着有網不走緩存,全部刷新數據;無網強制讀取緩存數據。

到現在爲止,關於Retrofit的基本用法和緩存設置已經瞭解了,寫點例子就已經會用了,接下來肯定就是進行封裝了,讓它更便於調用。

7. Retrofit 封裝

封裝Retrofit是爲了提供更方便的調用,更好的配置和使用。那麼看到開頭的Retrofit調用方式,大概有這麼幾個步驟: 
1. 創建OkHttpClient 
2. 創建Retrofit實例 
3. 獲取我們寫的API interface 
4. 在代碼中異步調用

那麼封裝時候也肯定從這幾步入手:

7.1 初始化OkHttpClient

創建OkHttpFactory類,初始化OkHttpClient並對外提供該實例的單例。並提供一個 HttpHelper 類用於提供所需要的參數,比如UA。 
這樣做的好處是,程序中會使用OkHttp做一些其他請求操作,比如下載、上傳等網絡操作,就可用共用一個OkHttpClient。

這裏使用枚舉生成單例。

enum OKHttpFactory {

  INSTANCE;

  private final OkHttpClient okHttpClient;

  private static final int TIMEOUT_READ = 25;
  private static final int TIMEOUT_CONNECTION = 25;

  OKHttpFactory() {
    //打印請求Log
    HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
    interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

    //緩存目錄
    Cache cache = new Cache(MyApplication.mContext.getCacheDir(), 10 * 1024 * 1024);

    okHttpClient = new OkHttpClient.Builder()
        //打印請求log
        .addInterceptor(interceptor)

        //stetho,可以在chrome中查看請求
        .addNetworkInterceptor(new StethoInterceptor())

        //添加UA
        .addInterceptor(new UserAgentInterceptor(HttpHelper.getUserAgent()))

        //必須是設置Cache目錄
        .cache(cache)

        //走緩存,兩個都要設置
        .addInterceptor(new OnOffLineCachedInterceptor())
        .addNetworkInterceptor(new OnOffLineCachedInterceptor())

        //失敗重連
        .retryOnConnectionFailure(true)

        //time out
        .readTimeout(TIMEOUT_READ, TimeUnit.SECONDS)
        .connectTimeout(TIMEOUT_CONNECTION, TimeUnit.SECONDS)

        .build();
  }

  public OkHttpClient getOkHttpClient() {
    return okHttpClient;
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

7.2 初始化Retrofit

依然使用枚舉創建單例:

public enum RetrofitClient implements ApiContants {
  INSTANCE;

  private final Retrofit retrofit;

  RetrofitClient() {
    retrofit = new Retrofit.Builder()
        //設置OKHttpClient
        .client(OKHttpFactory.INSTANCE.getOkHttpClient())

        //baseUrl
        .baseUrl(GITHUB_BASEURL)

        //gson轉化器
        .addConverterFactory(GsonConverterFactory.create())

        .build();
  }

  public Retrofit getRetrofit() {
    return retrofit;
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

7.3 創建ApiFactory類封裝所有API

如題,創建ApiFactory類管理所有的API interface,對外提供方法獲取他們,這樣調用時會方便很多,而且也便於修改。

public enum ApiFactory {
  INSTANCE;

  private final GitHubAPI gitHubAPI;
  private final AnotherAPI anotherAPI;

  ApiFactory() {
    gitHubAPI = RetrofitClient.INSTANCE.getRetrofit().create(GitHubAPI.class);
    anotherAPI = RetrofitClient.INSTANCE.getRetrofit().create(AnotherAPI.class);
  }

  public GitHubAPI gitHubAPI() {
    return gitHubAPI;
  }

  public AnotherAPI getAnotherAPI() {
    return anotherAPI;
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

這樣封裝在外部調用時會方便很多,比如:

    Call<User> userCall = ApiFactory.gitHubAPI().userInfo("baiiu");
  • 1
  • 1

封裝的代碼在GitHub上:retrofitLearn;

這樣封裝自己覺得還不錯,就可以開心的寫代碼啦。接下來說說一個神奇的工具。

8. Stetho 一個神奇的工具

首先:使用該工具需要翻牆。 不能翻牆的還是使用Charles吧。

  1. gradle中添加:
  compile 'com.facebook.stetho:stetho:1.3.1'
  compile 'com.facebook.stetho:stetho-okhttp3:1.3.1'
  • 1
  • 2
  • 1
  • 2
  1. 打開chrome,輸入chrome://inspect 
    點擊你的程序進行inspect,就可以直接使用Chrome進行抓包、調試你的應用啦。但是不能翻牆的話,你會看到一片空白。。。

結語:

畢竟Retrofit出來已經很久了,沒有的趕緊用用吧。在使用Retrofit時,深深趕緊到其對Http本身機制的應用之深,真的感覺到非常棒的應用體驗。用Retrofit作爲網絡框架,簡直就能感覺到它設計之美,更何況還能和RxJava結合起來,美到不行。


參考:這些參考都值得一看的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章