Android 網路請求框架Retrofit + OkHttp3 + coroutines + LiveData

語言: CN / TW / HK

一個好的網路框架需要有那些特點呢?

  • 請求 當然這個請求不單單是傳送請求這麼簡單,它包括請求相關的一系列配置是否簡易、傳送模式是否靈活切換、請求頭資訊是否易處理、請求引數是否易操作等等
  • 響應 一個好的網路請求框架肯定需要提高我們的生產力,保證我們程式的健壯性,那麼響應體和這些又有什麼關係呢? 容易轉換成不同需求的實體 是否能返回不同型別的結構。xml、json、text等等

總的來說就是易使用、易擴充套件、可讀性高


一、目的

  • 背景

    市場上現在存在的網路請求不在少數,從原生的HttpClient 到 OkHttp、Volley、xUtils 無疑都是將網路請求簡單化、方便、安全等用以提升開發的效率以及程式的質量。後面出現Retrofit ,將OkHttp3 進行了封裝,將請求API 介面化,將返回的資料結合GSON等各種轉換器轉換為直接面向開發的物件,大大的提升了我們的開發效率,為了解決Android UI/子執行緒 負責的功能場景方便切換,大家開始結合了RxJava, 這一操作直接將Retrofit + OkHttp3 + RxJava 組合的網路框架推上了熱門寫法,

  • 現狀

kotlin 的出現是Google 對於Android開發的重新定義,含糊不清的態度,既不丟棄Java又宣佈kotlin 是首選語言。在次基礎上結合jetpack框架,成了新時代開發的另一條選擇路徑,從始至終Google 沒有對Android推出過官方的設計模式,大家都是從高內聚、低耦合等方面推行著自己的設計模式,讓開發簡單、維護簡單、程式健壯。jetpack是Google 對設計模式的首次官方製造,重點可想而知,那在這個基礎上我們還需要使用之前的網路框架嗎?有沒有更加優秀的框架以及框架的組合使得我們的程式健壯、開發簡易呢?

  • jetpack

具體的jetpack不是這裡解釋的。其中ViewModel - LiveData 已經得到了廣大開發者的認可,ViewModel 和LiveData 的結合,使得Android中資料可控性變得更好,耦合度更低,簡單來說是官方將觀察者模式用於到了真個資料結構中,在這裡也是將要結合其他框架來完成網路框架的設計

  • coroutines

kotlin較Java的最大變化就是kotlin推出的coroutines (協程),協程完全可以替代RxJava, Thread、多級介面回撥等,並且有上下文及各種模式來迎合各種場景,具體不再這裡解釋,

  • 使用上述出現的幾個框架完成新的網路請求框架的封裝

二、組合框架

2.1 新增依賴

```kotlin

//LifeCycle implementation 'androidx.lifecycle:lifecycle-common:2.2.0' implementation 'androidx.lifecycle:lifecycle-runtime:2.2.0' implementation 'android.arch.lifecycle:extensions:2.2.0' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'

//Retrofit implementation "com.squareup.retrofit2:retrofit:2.9.0" implementation "com.squareup.okhttp3:logging-interceptor:4.2.0" implementation "com.squareup.retrofit2:converter-gson:2.9.0" implementation 'com.squareup.retrofit2:converter-scalars:2.6.2' //Coroutines implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7'

//Kotlin extensions for activity and fragments
implementation 'androidx.fragment:fragment-ktx:1.2.5'
implementation "androidx.activity:activity-ktx:1.1.0"

// Kotlin + coroutines
implementation "androidx.work:work-runtime-ktx:2.3.4"

// optional - RxJava2 support
implementation "androidx.work:work-rxjava2:2.3.4"

// optional - GCMNetworkManager support
implementation "androidx.work:work-gcm:2.3.4"

// optional - Test helpers
androidTestImplementation "androidx.work:work-testing:2.3.4"
implementation 'org.conscrypt:conscrypt-android:2.2.1'

```

具體根據需求新增

2.2 請求輔助類

  • 狀態管理

kotlin enum class Status { SUCCESS, ERROR, LOADING }

  • 請求結果處理類

```kotlin class Resource(val status: Status, val data: T?, val message: String?) { companion object { fun success(data: T?) = Resource(Status.SUCCESS, data, null) fun error(msg: String?, data: T?) = Resource(Status.ERROR, data, msg) fun loading(data: T?) = Resource(Status.LOADING, data, null) } }

```

2.3 使用Retrofit 建立API 介面、介面幫助類

將介面管理和請求放在不同的類檔案中,方便管理

  • API 介面

kotlin interface ApiService { @GET("{page}") suspend fun getGirls(@Path("page") page: Int): Girls }

資料類將傳到Demo中 Retrofit + OkHttp3 + coroutines + LiveData打造一款網路請求框架

  • API 介面類呼叫輔助類

kotlin class ApiHelper(private val apiService: ApiService) { suspend fun getGirls() = apiService.getGirls(1) }

2.4 建立Retrofit及OkHttp等網路框架請求幫助類

```kotlin object ServiceCreator { private val okHttpClient by lazy { OkHttpClient().newBuilder() } private val retrofit: Retrofit by lazy { val builder = Retrofit.Builder() .baseUrl("https://gank.io/api/v2/data/category/Girl/type/Girl/page/1/count/") .addConverterFactory(GsonConverterFactory.create()) val dispatcher = Dispatcher() dispatcher.maxRequests = 1 val httpLoggingInterceptor = HttpLoggingInterceptor() httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY okHttpClient .connectTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .addInterceptor(httpLoggingInterceptor) .addInterceptor(com.kpa.network.data.http.interceptor.HttpLoggingInterceptor()) .dispatcher(dispatcher) builder.client(okHttpClient.build()).build() }

fun <T> create(clazz: Class<T>): T = retrofit.create(clazz)

inline fun <reified T> createService(clazz: Class<T>): T =
    create(clazz)

} ```

使用懶載入,將需要的配置在此處配置好,inline 對函式再次呼叫,可以查一下這樣用的優點。

2.5 建立資料倉庫

資料倉庫的建立是為了能在此處處理資料,可能存在需要儲存或者重構的資料,也是將資料的處理和ViewModel分離開,專職做資料處理,ViewModel 做資料週轉

kotlin class MainRepository(private val apiHelper: ApiHelper) { suspend fun getGirls() = apiHelper.getGirls() }

2.6 ViewModel

一般在使用ViewModel 的時候都是於一個或者一組邏輯相關的頁面對應,將資料更加獨立、清晰

kotlin class MainViewModel(private val mainRepository: MainRepository) : ViewModel() { fun getGirls() = liveData(Dispatchers.IO) { emit(Resource.loading(null)) try { emit(Resource.success(mainRepository.getGirls())) } catch (e: Exception) { emit(Resource.error(e.message, null)) } } }

2.7 建立一個ViewMdoel的工廠,用來建立ViewMdoel

這樣做的好處在於可隨便傳遞引數等

kotlin class ViewModelFactory(private val apiHelper: ApiHelper) : ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(MainViewModel::class.java)) { return MainViewModel( MainRepository(apiHelper) ) as T } throw IllegalArgumentException("Unknown class name") } }

2.9 建立介面初始化幫助類

清晰的將介面初始化管理在一起,方便檢視

```kotlin object NetWorkHelper { val apiService = ServiceCreator.createService(ApiService::class.java) }

```

2.8 使用

在Activity、Fragment中 初始化

  • 初始化

```kotlin mainViewModel = ViewModelProviders.of(this, ViewModelFactory(ApiHelper(NetWorkHelper.apiService))).get(MainViewModel::class.java)

```

  • 使用資料

清晰的回撥狀態、處理不同場景

kotlin mainViewModel.getGirls().observe(this, Observer { it?.let { resource -> when (resource.status) { Status.SUCCESS -> { recyclerView.visibility = View.VISIBLE progressBar.visibility = View.GONE resource.data?.let { girls -> renderList(girls) } } Status.ERROR -> { progressBar.visibility = View.VISIBLE recyclerView.visibility = View.GONE } Status.LOADING -> { progressBar.visibility = View.VISIBLE Toast.makeText(this, it.message, Toast.LENGTH_LONG).show() } } } })

三、 總結

在開發中上可以更好的介面Databing 等元件、更加優雅的開發,對於資料的處理ViewMdoel 的好處真的太多了,可以多瞭解一下,

Demo 下載