一步步封裝實現本身的網絡請求框架 2.0

1、前言

在今年的二月份,我在發佈本 Repo:基於 ViewModel + LiveData + Retrofit + RxJava 封裝的網絡請求框架 的同時,也寫了一篇博客介紹了項目特點以及實現思路:ViewModel_Retrofit_RxJava_1.0,詳細地介紹了一步步封裝的過程,也陸續得到了一些讀者的正向反饋,讓我意識到了一些不足點,而到如今六月份,公司的項目也逐漸地轉爲了 Kotlin,所以也促使我來寫第二篇博客git

2、變化

本 Repo 現現在對應着兩個版本github

  • 1.0 版本即 master 分支,是使用 Java 語言寫的,也是最初始的版本,其實現思路能夠看這裏:ViewModel_Retrofit_RxJava_1.0,用了比較大的篇幅介紹了我封裝此網絡請求框架的思路,建議讀者首先去看下該文章json

  • 2.0 版本即 kotlin 分支,顧名思義,我用 Kotlin 重寫了一遍,固然也不只僅只是簡單的轉換了下語言而已,也解決了 1.0 版本不太理想或者不太合理的地方,也得益於 Kotlin 語言的簡潔性,使得整個基礎庫更加得短小精悍,表達能力也顯得更增強大服務器

3、優化點

3.一、BaseView

看到個人第一篇博客的同窗應該知道,在請求網絡的過程當中界面層的一些通用操做(startLoading、dismissLoading、showToast)是須要由 BaseActivity 與 BaseFragment 經過監聽 LiveData 的數據變化來完成的,1.0 版本受限於 JDK 版本,在 BaseActivity 與 BaseFragment 須要分別寫較多的重複代碼而沒法提取到接口中,而經過 Kotlin 來完成的話就會相對簡潔不少網絡

/** * 做者:leavesC * 時間:2019/5/31 9:38 * 描述: */
interface IBaseViewModelEvent {

    fun showLoading(msg: String)

    fun showLoading() {
        showLoading("")
    }

    fun dismissLoading()

    fun showToast(msg: String)

    fun finishView()

    fun pop()

}

interface IIBaseViewModelEventObserver : IBaseViewModelEvent {

    fun initViewModel(): BaseViewModel? {
        return null
    }

    fun initViewModelList(): MutableList<BaseViewModel>? {
        return null
    }

    fun getLifecycleOwner(): LifecycleOwner

    fun initViewModelEvent() {
        var list: MutableList<BaseViewModel>? = null
        val initViewModelList = initViewModelList()
        if (initViewModelList.isNullOrEmpty()) {
            val baseViewModel = initViewModel()
            baseViewModel?.let {
                list = mutableListOf(baseViewModel)
            }
        } else {
            list = initViewModelList
        }
        list?.let {
            observeEvent(list!!)
        }
    }

    fun observeEvent(viewModelList: MutableList<BaseViewModel>) {
        for (viewModel in viewModelList) {
            viewModel.baseActionEvent.observe(getLifecycleOwner(), Observer { it ->
                it?.let {
                    when (it.action) {
                        BaseViewModelEvent.SHOW_LOADING_DIALOG -> {
                            showLoading(it.message)
                        }
                        BaseViewModelEvent.DISMISS_LOADING_DIALOG -> {
                            dismissLoading()
                        }
                        BaseViewModelEvent.SHOW_TOAST -> {
                            showToast(it.message)
                        }
                        BaseViewModelEvent.FINISH -> {
                            finishView()
                        }
                        BaseViewModelEvent.POP -> {
                            pop()
                        }
                    }
                }
            })
        }
    }

    fun getLContext(): Context

    fun <T> startActivity(clazz: Class<T>) {
        getLContext().startActivity(Intent(getLContext(), clazz))
    }

    override fun showToast(msg: String) {
        Toast.makeText(getLContext(), msg, Toast.LENGTH_SHORT).show()
    }

}
複製代碼

BaseActivity 中只要實現幾個特定方法便可,BaseFragment 也同樣如此app

abstract class BaseActivity : AppCompatActivity(), IIBaseViewModelEventObserver {

    private var loadDialog: ProgressDialog? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        initViewModelEvent()
    }

    override fun getLContext(): Context = this

    override fun getLifecycleOwner(): LifecycleOwner = this

    override fun showLoading(msg: String) {
        if (loadDialog == null) {
            loadDialog = ProgressDialog(getLContext())
            loadDialog!!.setCancelable(false)
            loadDialog!!.setCanceledOnTouchOutside(false)
        }
        loadDialog?.let {
            if (!it.isShowing) {
                it.show()
            }
        }
    }

    override fun dismissLoading() {
        loadDialog?.let {
            if (it.isShowing) {
                it.dismiss()
            }
        }
    }

    override fun finishView() {
        finish()
    }

    override fun pop() {

    }

    fun <T : BaseViewModel> getViewModel(clazz: Class<T>) =
            ViewModelProviders.of(this).get(clazz)

}
複製代碼

3.二、BaseRemoteDataSource

在 1.0 版本中,RetrofitManagement 與 BaseRemoteDataSource 之間牽扯較多,BaseRemoteDataSource 調用了幾個本應不應由 RetrofitManagement 來實現的方法,使得邏輯較爲複雜凌亂框架

在 2.0 版本中,RetrofitManagement 變爲專職只負責提供 ApiService.class 對象, 具體的網絡請求調用以及數據解析都只由 BaseRemoteDataSource 來中轉控制ide

@SuppressLint("CheckResult")
    private fun <T> execute(observable: Observable<BaseResponse<T>>, observer: Observer<OptionT<T>>, quietly: Boolean) {
        observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe {
                    if (!quietly) {
                        showLoading()
                    }
                }.doFinally {
                    dismissLoading()
                }.flatMap(object : Function<BaseResponse<T>, ObservableSource<OptionT<T>>> {
                    override fun apply(t: BaseResponse<T>): ObservableSource<OptionT<T>> {
                        when {
                            t.code == HttpConfig.CODE_SUCCESS || t.message == "OK" -> {
                                val optional: OptionT<T> = OptionT(t.data)
                                return createData(optional)
                            }
                            else -> {
                                throw ServerResultException(t.message ?: "未知錯誤", t.code)
                            }
                        }
                    }
                }).subscribeWith(observer)
    }

    private fun <T> createData(t: T): Observable<T> {
        return Observable.create { emitter ->
            try {
                emitter.onNext(t)
                emitter.onComplete()
            } catch (e: Exception) {
                emitter.onError(e)
            }
        }
    }
複製代碼

3.三、可空性

有時候,在請求一個單純修改狀態值的接口時,服務器返回的數據多是這樣的,此時接口只是經過 code 來反饋該接口已經請求成功,而不須要返回特定數據,所以 data 直接爲 null優化

{
	    "code": 200,
	    "msg": "ok",
	    "data": null
    }
複製代碼

這就會衍生出一個問題,因爲 RxJava 2.0 版本後不容許出現 emitter.onNext(null) 傳遞 null 值的狀況,因此致使數據傳遞鏈路就會中斷,雖說此時只要要求後臺同事返回一個空字符串便可解決問題,可有時候溝通並不老是那麼理想……ui

所以就須要 app 端本身來解決了,2.0 版本是經過多加一個包裝類 OptionT 來解決該問題的

class OptionT<T>(val value: T)
複製代碼

在解析數據時,將咱們實際須要的數據 data 包裝在 OptionT 當中,此時就無需關心 data 是否爲 null ,

override fun apply(t: BaseResponse<T>): ObservableSource<OptionT<T>> {
            when {
                t.code == HttpConfig.CODE_SUCCESS || t.message == "OK" -> {
                    val optional: OptionT<T> = OptionT(t.data)
                    return createData(optional)
                }
                else -> {
                    throw ServerResultException(t.message ?: "未知錯誤", t.code)
                }
            }
    }

    private fun <T> createData(t: T): Observable<T> {
        return Observable.create { emitter ->
            try {
                emitter.onNext(t)
                emitter.onComplete()
            } catch (e: Exception) {
                emitter.onError(e)
            }
        }
    }
複製代碼

在 BaseSubscriber 中再將 data 取出來並傳遞給 RequestCallback ,以此規避 null 值不可傳遞的問題

class BaseSubscriber<T> constructor(private val requestCallback: RequestCallback<T>) :
    DisposableObserver<OptionT<T>>() {

    override fun onNext(t: OptionT<T>) {
        requestCallback.onSuccess(t.value)
    }

    override fun onError(e: Throwable) {
        e.printStackTrace()
        val msg = e.message ?: "未知錯誤"
        if (requestCallback is RequestMultiplyCallback) {
            if (e is BaseException) {
                requestCallback.onFail(e)
            } else {
                requestCallback.onFail(ServerResultException(msg))
            }
        } else {
            ToastHolder.showToast(msg = msg)
        }
    }

    override fun onComplete() {

    }

}
複製代碼

其實經過 OptionT 這個名字,不少讀者應該就能夠聯想到 JDK 1.8 新增的 Option 類了吧?其實此處就是借鑑該了其思想

我以爲有必要介紹的優化點就以上三個了,其它地方就留待讀者本身去發現了

4、Demo

爲了方便讀者理解,這裏我也提供了一個 Demo 供讀者參考,2.0 版本其自己就是一個完整的天氣預報App,讀者能夠自行下載運行

App下載:Weather

相關文章
相關標籤/搜索