Retrofit + OkHttp3 + coroutines + LiveData打造一款網絡請求框架

一個好的網絡框架須要有那些特色呢?java

  • 請求

固然這個請求不僅僅是發送請求這麼簡單,它包括請求相關的一系列配置是否簡易、發送模式是否靈活切換、請求頭信息是否易處理、請求參數是否易操做等等android

  • 響應

一個好的網絡請求框架確定須要提升咱們的生產力,保證咱們程序的健壯性,那麼響應體和這些又有什麼關係呢? 容易轉換成不一樣需求的實體 是否能返回不一樣類型的結構。xml、json、text等等git

總的來講就是易使用、易擴展、可讀性高github


1、目的

  • 背景

市場上如今存在的網絡請求不在少數,從原生的HttpClient 到 OkHttp、Volley、xUtils 無疑都是將網絡請求簡單化、方便、安全等用以提高開發的效率以及程序的質量。後面出現Retrofit ,將OkHttp3 進行了封裝,將請求API 接口化,將返回的數據結合GSON等各類轉換器轉換爲直接面向開發的對象,大大的提高了咱們的開發效率,爲了解決Android UI/子線程 負責的功能場景方便切換,你們開始結合了RxJava, 這一操做直接將Retrofit + OkHttp3 + RxJava 組合的網絡框架推上了熱門寫法,json

  • 現狀

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

  • jetpack

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

  • coroutines

kotlin較Java的最大變化就是kotlin推出的coroutines (協程),協程徹底能夠替代RxJava, Thread、多級接口回調等,而且有上下文及各類模式來迎合各類場景,具體再也不這裏解釋,安全

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

2、組合框架

2.1 添加依賴

//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'

複製代碼

具體根據需求添加markdown

2.2 請求輔助類

  • 狀態管理
enum class Status {
    SUCCESS,
    ERROR,
    LOADING
}
複製代碼
  • 請求結果處理類
class Resource<out T>(val status: Status, val data: T?, val message: String?) {
    companion object {
        fun <T> success(data: T?) = Resource(Status.SUCCESS, data, null)
        fun <T> error(msg: String?, data: T?) = Resource(Status.ERROR, data, msg)
        fun <T> loading(data: T?) = Resource(Status.LOADING, data, null)
    }
}

複製代碼

2.3 使用Retrofit 建立API 接口、接口幫助類

將接口管理和請求放在不一樣的類文件中,方便管理網絡

  • API 接口
interface ApiService {
    @GET("{page}")
    suspend fun getGirls(@Path("page") page: Int): Girls
}
複製代碼

數據類將傳到Demo中 Retrofit + OkHttp3 + coroutines + LiveData打造一款網絡請求框架

  • API 接口類調用輔助類
class ApiHelper(private val apiService: ApiService) {
    suspend fun getGirls() = apiService.getGirls(1)
}
複製代碼

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

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 作數據週轉

class MainRepository(private val apiHelper: ApiHelper) {
    suspend fun getGirls() = apiHelper.getGirls()
}
複製代碼

2.6 ViewModel

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

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

這樣作的好處在於可隨便傳遞參數等

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.8 建立接口初始化幫助類

清晰的將接口初始化管理在一塊兒,方便查看

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

複製代碼

2.9 使用

在Activity、Fragment中 初始化

  • 初始化
mainViewModel =
            ViewModelProviders.of(this, ViewModelFactory(ApiHelper(NetWorkHelper.apiService))).get(MainViewModel::class.java)

複製代碼
  • 使用數據

清晰的回調狀態、處理不一樣場景

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()
                    }
                }
            }
        })
複製代碼

3、 總結

在開發中上能夠更好的接口Databing 等組件、更加優雅的開發,對於數據的處理ViewMdoel 的好處真的太多了,能夠多瞭解一下,

Demo 下載

相關文章
相關標籤/搜索