Kotlin封裝RxJava與Retrofit


代碼地址:https://github.com/DarkPointK/RxTrofit.git
前言

       Retrofit是Square公司開發的一個類型安全的Java和Android 的REST客戶端庫。來自官網的介紹:

A type-safe HTTP client for Android and Java

Rest API是一種軟件設計風格,服務器作為資源存放地。客戶端去請求GET,PUT, POST,DELETE資源。並且是無狀態的,沒有session的參與。

  • retrofit模型如下:
  1. POJO或模型實體類 : 從服務器獲取的JSON數據將被填充到這種類的實例中。
  2. 接口 : 我們需要創建一個接口來管理像GET,POST...等請求的URL,這是一個服務類。
  3. RestAdapter類 : 這是一個REST客戶端(Rest Client)類,retrofit中默認用的是Gson來解析JSON數據,你也可以設置自己的JSON解析器。

Retrofit是基於OkHttp的網絡接口的封裝,之所以被中小型項目所推行使用,還是得力於它的相對規范化、安全性、可操作性

 

RxJava 在 GitHub 主頁上的用這樣一句話形容自己是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一個在 Java VM 上使用可觀測的序列來組成異步的、基於事件的程序的庫)。這就是 RxJava ,概括得非常精准,但這句話更像是對RxJava的一個總結。其實, 簡單來說RxJava 的本質可以壓縮為異步這一個詞。說到根上,它就是一個實現異步操作的庫,而別的定語都是基於這之上的。用我個人的話來說,RxJava既是AsyncTask 的終極形態。

准備工作

       前面,我們簡單的了解了一下Retrofit與RxJava,本文的着重點在大家已經會用的前提下,還是要和大家探討如何去應用以及封裝他們。想要在項目中使用他們,第一步便是在Gradle中添加相應的Dependencies:

    compile 'com.squareup.retrofit2:retrofit:2.3.0'

    compile 'com.squareup.retrofit2:converter-gson:2.3.0'//retrofit2的Json轉換器(默認用Gson)

    compile 'com.squareup.okhttp3:okhttp:3.9.0'

    compile 'com.squareup.okhttp3:logging-interceptor:3.9.0'//okhttp提供的請求日志攔截器

    compile 'io.reactivex:rxjava:1.3.0'

    compile 'io.reactivex:rxandroid:1.2.1'

compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'

簡單封裝

接下來,創建必要的類與接口,當然,別忘了manifests中添加權限:

<uses-permission android:name="android.permission.INTERNET"/>

新建一個提供給Retrofit初始化,用於描述服務端接口的Interface,我在這里調用的是豆瓣的一個公開接口:

1 interface HttpInterface {
2 
3     @GET("top250")
4 
5     fun getTopMovie(@Query("start") start: Int, @Query("count") count: Int): Observable<GetMovie>
6 
7 }

這里定義了一個GET目錄top250,參數為start,count,返回值則是一個Observable綁定了一個接口返回數據的實體類

 

接着新建一個用於初始化Retrofit封裝請求的方法類:

 1 class HttpMethod {
 2 
 3     companion object {
 4 
 5         val httpMethod: HttpMethod = Holder.INSTANCE
 6 
 7     }
 8 
 9     private object Holder {
10 
11         val INSTANCE = HttpMethod()
12 
13     }
14 
15 }
16 
17  

由於網絡請求在業務邏輯中將頻繁的被調用,所以需要我們在這里創建一個當前類的單例,以避免被多次實例化。

 

       當我們創建好單例后,便可以放心的來初始化我們即將需要用到的3個關鍵的成員變量了,retrofit的回調適配器我們在這選用RxJavaCallAdapterFactory:

 1 private var okclient = OkHttpClient.Builder()
 2 
 3      .addInterceptor(HttpLoggingInterceptor().
 4 
 5 setLevel(HttpLoggingInterceptor.Level.BODY))
 6 
 7    .addNetworkInterceptor(OAuthIntercepter())
 8 
 9      connectTimeout(8, TimeUnit.SECONDS)
10 
11      readTimeout(15, TimeUnit.SECONDS)
12 
13      writeTimeout(15, TimeUnit.SECONDS)
14 
15      build()
16 
17  
18 
19 private var retrofit: Retrofit = Retrofit.Builder()
20 
21      .client(okclient)
22 
23      .baseUrl("https://api.douban.com/v2/movie/")
24 
25      .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
26 
27      .addConverterFactory(GsonConverterFactory
28 
29 .create(GsonBuilder().serializeNulls().setLenient().create())
30 
31 )
32 
33      .build()
34 
35  
36 
37 private var httpInterface = retrofit.create(HttpInterface::class.java)
38 
39  

 

我們首先Builder了一個OkhttpClient對象,並為其添加了一個請求日志的攔截器,緊接着初始化了Retrofit為其添加RxJavaCallAdapter,最后以HttpInterface為參數將retrofit創建為HttpInterface,這樣我們就可以用httpInterface封裝一個調用之前所描述的getTopMovie接口方法,在這個方法中我們會將Retrofit完成請求返回的被觀察者訂閱起來,在這個過程中我們可以對返回的數據流通過RxJava豐富的操作符進行自由的處理。

 

       定義一個getTopMovie方法用於在業務邏輯中調用retrofit接口,這里我們需要三個參數,其中前兩個為GET請求所需要的參數,而subscriber用來訂閱retrofit接口返回的Observable:

1 fun getTopMovie(start: Int, count: Int, subscriber: Subscriber<GetMovie>) {}

這樣我們便可以在客戶端的業務邏輯中通過調用HttpMethod. INSTANCE .getTopMovie(…)來請求接口,但是此時我們還沒有將Observable訂閱上,所以現在我們需要完善下這個方法:

1 fun getTopMovie(start: Int, count: Int, subscriber: Subscriber<GetMovie>) {
2 
3         toSubscriber(httpInterface.getTopMovie(start,count),subscriber)
4 
5     }

可以看到我們在方法體中添加了一句toSubscriber(…),至此我們便將subscriber訂閱上httpInterface.getTopMovie(…)返回的Observable了,至於這個subscriber參數具體如何實現,在下一節將會介紹,我們先來了解下這個toSubscriber的工作是如何實現的:

 1 private fun toSubscriber (observable: Observable<T>, subscriber: Subscriber<T>) {
 2 
 3         observable.subscribeOn(Schedulers.io())
 4 
 5                 .unsubscribeOn(Schedulers.io())
 6 
 7                 .observeOn(AndroidSchedulers.mainThread())
 8 
 9                 .doOnError {}
10 
11                 .subscribe(subscriber)
12 
13     }

熟悉RxJava的碼友應該一眼變冷看出這是一段典型的訂閱流程,是的,這是通過RxJava流式編程實現最簡單的訂閱事件。

至此,我們便已經可以再業務邏輯中正常使用getTopMovie(…)了:

 1 HttpMethod.httpMethod.getTopMovie(1,1,object :Subscriber<GetMovie>(){
 2 
 3             override fun onNext(t: GetMovie?) {
 4 
 5             }
 6 
 7  
 8 
 9             override fun onCompleted() {
10 
11             }
12 
13  
14 
15             override fun onError(e: Throwable?) {
16 
17             }
18 
19  
20 
21         })
進階用法

       我們已經知道如何簡單的方式通過RxJava調用Retrofit接口,但這往往不能的滿足我們的追求。

在項目中,往往我們會定義這樣的返回體結構:

{

    "code": 1,

    "data": [ ],

    "msg": "success"

}

 

我們會在所有接口得到3個全局相應參數,其中code代表結果碼,msg代表附加信息,而data則是對應的響應數據會因接口而異,我們往往需要每個接口根據code去判斷接下來的操作,那么我們應該怎樣封裝才能讓每個統一進行處理呢,接下來我們改一改前面的訂閱流程:

data class GetMovie<T>(

              @SerializedName("count") var count: Int?, //20

              @SerializedName("start") var start: Int?, //0

              @SerializedName("total") var total: Int?, //250

              @SerializedName("subjects") var subjects: T?,

              @SerializedName("title") var title: String? //豆瓣電影Top250

)

 

這里我們切當subjects以外的參數為固有的全局響應參數,而subjects的List元素類型則不定,我們需要顯示的綁定GetMovie以在訂閱時可以取得全局響應參數而不用關心subjects的類型,改動的包括接口定義在內用到實體類泛型的地方:

interface HttpInterface {

    @GET("top250")

    fun getTopMovie(@Query("start") start: Int, @Query("count") count: Int): Observable<GetMovie<List<Subject>>>

}

 

接口表示我們獲取到的返回數據將會是GetMovie類的結構,並且泛型指出subjects的類型是個Subject的List,接下來在將HttpMethod中封裝請求的方法也修改下:

fun getTopMovie(start: Int, count: Int, subscriber: Subscriber<List<Subject>>) {

        toSubscriber(httpInterface.getTopMovie(start, count), subscriber)

    }

由於我們需要在RxJava處理數據的流程中,統一對全局響應參數進行判斷,所以訂閱的流程便成為了我們工作主要進行的場所:

 1 private fun <T> toSubscriber(observable: Observable<GetMovie<T>>, subscriber: Subscriber<T>) {
 2 
 3         observable
 4 
 5                 .compose {
 6 
 7                     it.flatMap { result ->
 8 
 9                         if (result.count!! > 0)
10 
11                             createData(result.subjects)
12 
13                         else
14 
15                             Observable.error(Throwable(""))
16 
17                     }
18 
19                 }
20 
21                 .subscribeOn(Schedulers.io())
22 
23                 .unsubscribeOn(Schedulers.io())
24 
25                 .observeOn(AndroidSchedulers.mainThread())
26 
27                 .doOnError {}
28 
29                 .subscribe(subscriber)
30 
31     }

可以看到這里我們使用了兩個RxJava的操作符分別是compose與flatMap,經過flatMap的轉換返回一個新的被觀察者綁定的數據便是subjects

 1 private fun <T> createData(data: T): Observable<T> {
 2 
 3         return Observable.create { subscriber ->
 4 
 5             try {
 6 
 7                 subscriber.onNext(data)
 8 
 9                 subscriber.onCompleted()
10 
11             } catch (e: Exception) {
12 
13                 subscriber.onError(e)
14 
15             }
16 
17         }
18 
19     }

最后,業務邏輯中這樣調用:

 1  HttpMethod.httpMethod.getTopMovie(1,1,object :Subscriber<List<Subject>>(){
 2 
 3             override fun onNext(t: List<Subject>?) {
 4 
 5                 sample_text.text =t?.get(0)?.originalTitle
 6 
 7             }
 8 
 9             override fun onCompleted() {
10 
11             }
12 
13             override fun onError(e: Throwable?) {
14 
15             }
16 
17         })

 

傳入參數及Subscriber,經過compose轉換在onNext中得到List<Subject>。

至此對RxJava與Retrofit的封裝告一段落了,通過compose操作符,我們可以隨心的去操作數據流,以得到我們想要的數據,並且還有其他豐富的操作符,例如repeatWhen,retryWhen ,doOnError,delay等等實用的方法,在稍后會和大家做進一步介紹。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM