Kotlin Common篇之六 Retrofit + OkHttp + Rxjava

首發(fā)于公眾號: DSGtalk1989

這兩尊大佛悴品,基本都是結(jié)伴出現(xiàn)的贷币,我們先什么都不管把依賴添加進(jìn)來,考慮到可能存在的版本號問題握联,之后將不再出現(xiàn)具體的版本號桦沉。

//Retrofit
implementation "com.squareup.okhttp3:logging-interceptor:${okhttp3_version}"
  implementation "com.squareup.retrofit2:retrofit:${retrofit_version}"
  implementation "com.squareup.retrofit2:converter-gson:${retrofit_version}"
  implementation "com.squareup.retrofit2:adapter-rxjava2:${retrofit_version}"
  
//RxJava
implementation "io.reactivex.rxjava2:rxjava:${rx_version}"  

按照以往的習(xí)慣,我們需要一個(gè)類拴疤,來初始化OkhttpRetrofit。并且加上一些我們需要的攔截独泞,比如日志等等呐矾。這些東西放在類的初始化中會(huì)比較合適。

class RetrofitFactory {
        val retrofit: Retrofit
      
        init {
            //打印請求log
            val logging = HttpLoggingInterceptor()
            logging.level = if (BuildConfig.DEBUG) {
                HttpLoggingInterceptor.Level.BODY
            } else {
                HttpLoggingInterceptor.Level.NONE
            }
            val mOkHttpClient = OkHttpClient.Builder()
                .addInterceptor(logging)
                .addInterceptor(headerInterceptor())
                .build()
      
            retrofit = Retrofit.Builder()
                .baseUrl(AppConfig.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(mOkHttpClient)
                .build()
        }
        
      /**
       * 攔截頭部
       */
      private fun headerInterceptor(): Interceptor {
          return Interceptor { chain ->
              var request = chain.request()
              //TODO 根據(jù)項(xiàng)目適配調(diào)整
              request = request.newBuilder()
                  .addHeader("key", "value")
                  .build()
  
              chain.proceed(request)
          }
      }
}

現(xiàn)在初始化完了懦砂,我們怎么去使用呢蜒犯。一般在java中都會(huì)去構(gòu)建一個(gè)單例來持有一個(gè)全局的Retrofit對象。

所以需要用到kotlin的單例知識荞膘,這里涉及到的單例模式我們可以參考Kotlin的伴生對象()罚随。

companion object {
      //Double Check
      val instance: RetrofitFactory by lazy {
          RetrofitFactory()
      }

      fun <T> createService(service: Class<T>): T {
          return instance.retrofit.create(service)
      }

}

我們直接使用lazy委托的方式實(shí)現(xiàn)單例,見委托羽资。這樣我們在每一次想要獲取到retrofit接口時(shí)淘菩,直接調(diào)用RetrofitFactory.createService(apiInterface)即可。

那么既然我們還引入了Rxjava,要如何結(jié)合起來使用呢潮改?

先自定義一個(gè)全局網(wǎng)絡(luò)請求的Observer狭郑,封裝網(wǎng)絡(luò)請求的各種情況,成功汇在,失敗翰萨,完成等等。

abstract class ResultObserver<T> : Observer<Response<T>> {

      override fun onSubscribe(d: Disposable) {
          if (!d.isDisposed) {
              onRequestStart()
          }
      }
  
      override fun onNext(reposnse: Response<T>) {
          onRequestEnd()
          if (reposnse.isSuccessful) {
              try {
                  onSuccess(reposnse.body())
              } catch (e: Exception) {
                  e.printStackTrace()
              }
  
          } else {
              try {
                  onBusinessFail(reposnse.code(), reposnse.message())
              } catch (e: Exception) {
                  e.printStackTrace()
              }
  
          }
      }
  
      override fun onError(e: Throwable) {
          onRequestEnd()
          try {
              if (e is ConnectException
                  || e is TimeoutException
                  || e is NetworkErrorException
                  || e is UnknownHostException
              ) {
                  onFailure(e, true)
              } else {
                  onFailure(e, false)
              }
          } catch (e1: Exception) {
              e1.printStackTrace()
          }
  
      }
  
      override fun onComplete() {}
  
  
      /**
       * 請求開始
       */
      open fun onRequestStart() {
  
      }
  
      /**
       * 請求結(jié)束
       */
      open fun onRequestEnd() {
  
      }
  
      /**
       * 返回成功
       *
       * @param result
       * @throws Exception
       */
      @Throws(Exception::class)
      abstract fun onSuccess(result: T?)
  
      /**
       * 返回失敗
       *
       * @param e
       * @param isNetWorkError 是否是網(wǎng)絡(luò)錯(cuò)誤
       * @throws Exception
       */
      @Throws(Exception::class)
      abstract fun onFailure(e: Throwable, isNetWorkError: Boolean)
  
      /**
       * 業(yè)務(wù)錯(cuò)誤
       * 返回成功了,但是code錯(cuò)誤
       *
       * @param t
       * @throws Exception
       */
      @Throws(Exception::class)
      open fun onBusinessFail(code: Int, message: String) {
      }
  }

首先是繼承Observer糕殉,復(fù)寫四個(gè)接口方法:onSubscribe``onNext``onError``onComplete亩鬼。然后加入了使用需要復(fù)寫的請求結(jié)果業(yè)務(wù)方法:成功onSuccess,失敗onFailure阿蝶。以及給出了一些定制化選項(xiàng):開始請求onRequestStart雳锋,請求結(jié)束onRequestEnd,業(yè)務(wù)錯(cuò)誤onBusinessFail

我們在RetrofitFactory文件中加入針對Observable的擴(kuò)展函數(shù)

  fun <T> Observable<Response<T>>.executeResult(subscriber: ResultObserver<T>){
      this.subscribeOn(Schedulers.io())
              .observeOn(AndroidSchedulers.mainThread())
              .subscribe(subscriber)
  }

這樣赡磅,我們直接通過Observable對象來調(diào)用executeResult方法魄缚,即可控制生命周期。

我們來寫一個(gè)網(wǎng)絡(luò)請求的例子:

  • 定義接口

     interface UserService {
          @GET("user")
          fun getPersonInfo(): Observable<Response<User>>
     }
    

    很好理解焚廊,跟java基本一致冶匹。

  • 調(diào)接口請求網(wǎng)絡(luò)

     RetrofitFactory.createService(WeatherService::class.java).getWeatherData()
         .executeResult(object : ResultObserver<WeatherData>() {
             override fun onSuccess(result: WeatherData?) {
             }
    
             override fun onFailure(e: Throwable, isNetWorkError: Boolean) {
             }
       })
    

就這兩步,就這么簡單咆瘟。

如果希望能夠再做更多的擴(kuò)展嚼隘,能夠讓我們的框架可以應(yīng)對更多的可能。比如我們需要將服務(wù)器返回的結(jié)果做一些轉(zhuǎn)換袒餐,將json數(shù)據(jù)轉(zhuǎn)成我們需要的單個(gè)對象或者是數(shù)組飞蛹,然后做一些修改繼續(xù)使用。我們可以借助RxjavaflatMap操作符灸眼,為此打造一個(gè)FlatMapUtil卧檐,專門處理類型切換。

實(shí)際上接下去的更多的和Rxjava有關(guān)焰宣,我們先來看個(gè)簡單的任務(wù)組合

 //數(shù)據(jù)庫任務(wù)
 val dbTask : Observable<User> = UsersDatabase.instance.userDao().getUserById("test")
 //網(wǎng)絡(luò)任務(wù)
 val netWorkTask : Observable<Response<WeatherData>>= RetrofitFactory.createService(WeatherService::class.java).getWeatherData()
 //這兩個(gè)任務(wù)合并起來
 val zipTask = Observable.zip(dbTask, netWorkTask, BiFunction<User, Response<WeatherData>, Response<WeatherData>>{
          user, response ->
             response
      })

我們需要做兩個(gè)任務(wù)霉囚,而且必須要兩個(gè)任務(wù)都做完了之后才能繼續(xù)。

現(xiàn)在我們需要將網(wǎng)絡(luò)中獲得的東西做一些類型轉(zhuǎn)換匕积。我們活著放在第二步的網(wǎng)絡(luò)任務(wù)中盈罐,活著放在合并任務(wù)的BiFunction中,其實(shí)最終差別不大闪唆。

val netWorkTask =
      RetrofitFactory.createService(WeatherService::class.java).getWeatherData()
          .flatMap {
              val user = User(it.body()!!.info, "Tom")
              val successful = it.isSuccessful
              val response = if(successful) retrofit2.Response.success(user) else it
              ObservableSource<User> { observer ->
                  if(successful){
                      observer.onNext(user)
                  } else {
                      observer.onError(Throwable(response.code().toString(), Throwable(response.errorBody().toString())))
                  }
              }
          }

FlatMapUtil就是針對我們需要的flatMap做了一層封裝盅粪,我們只需要去實(shí)現(xiàn)對象的轉(zhuǎn)換,不需要考慮觀察者的傳遞和異常處理悄蕾。

/**
* Response的實(shí)體類型轉(zhuǎn)換
*/
class FlatMapResponse2ResponseObject<T, R>(
      private val response: Response<T>,
      private val conversionCallBack: FlatConversionObjectInterface<T, R>
  ) : ObservableSource<Response<R>> {
      override fun subscribe(observer: Observer<in Response<R>>) {
          if (response.isSuccessful) {
              val result = conversionCallBack.onConversion(response.body())
              observer.onNext(Response.success(result))
  
          } else {
              observer.onError(Throwable(response.code().toString(), Throwable(response.errorBody().toString())))
          }
  
      }
  }
  
  interface FlatConversionObjectInterface<T, R> {
      fun onConversion(t: T?): R
  }

所以上面的代碼可以簡化成

val netWorkTask =
       RetrofitFactory.createService(WeatherService::class.java).getWeatherData()
          .flatMap {
              FlatMapResponse2ResponseObject(it, object: FlatConversionObjectInterface<WeatherData, User>{
                  override fun onConversion(t: WeatherData?): User {
                      return User(it.body()!!.info, "Tom")
                  }
              })
          }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末票顾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌库物,老刑警劉巖霸旗,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異戚揭,居然都是意外死亡诱告,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門民晒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來精居,“玉大人,你說我怎么就攤上這事潜必⊙プ耍” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵磁滚,是天一觀的道長佛吓。 經(jīng)常有香客問我,道長垂攘,這世上最難降的妖魔是什么维雇? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮晒他,結(jié)果婚禮上吱型,老公的妹妹穿的比我還像新娘。我一直安慰自己陨仅,他們只是感情好津滞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著灼伤,像睡著了一般触徐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上狐赡,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天撞鹉,我揣著相機(jī)與錄音,去河邊找鬼猾警。 笑死孔祸,一個(gè)胖子當(dāng)著我的面吹牛隆敢,可吹牛的內(nèi)容都是我干的发皿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼拂蝎,長吁一口氣:“原來是場噩夢啊……” “哼穴墅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤玄货,失蹤者是張志新(化名)和其女友劉穎皇钞,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體松捉,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡夹界,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了隘世。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片可柿。...
    茶點(diǎn)故事閱讀 40,117評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖丙者,靈堂內(nèi)的尸體忽然破棺而出复斥,到底是詐尸還是另有隱情,我是刑警寧澤械媒,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布目锭,位于F島的核電站,受9級特大地震影響纷捞,放射性物質(zhì)發(fā)生泄漏痢虹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一兰绣、第九天 我趴在偏房一處隱蔽的房頂上張望世分。 院中可真熱鬧,春花似錦缀辩、人聲如沸臭埋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓢阴。三九已至,卻和暖如春健无,著一層夾襖步出監(jiān)牢的瞬間荣恐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工累贤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叠穆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓臼膏,卻偏偏與公主長得像硼被,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子渗磅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評論 2 355