像使用gradle同樣,在kotlin中進行網絡請求

前言

上篇文章(DSL形式的基於retrofit、協程的網絡請求封裝)介紹了,如何基於retorfit、協程去開發一個dsl形式的網絡請求,可是封裝完後的寫法並不足夠DSL,有童鞋表示看起來仍是如rxjava同樣的鏈式請求而已。接下來便封裝一個標準的DSL網絡請求方式。html

DSL是Domain-specific language(領域特定語言)的縮寫,維基百科的定義是指的是專一於某個應用程序領域的計算機語言。
這種說法看起來很抽象,其實你們很經常使用的gradle就是DSL最經常使用體現,能夠看一下android project中的build.gradle:
java

image

android{}, dependencies{}這種都是DSL的表現形式,相對於傳統的寫法更加簡潔、表現內容更加明顯,如配置文件般的去執行方法,這也是爲何推薦DSL寫法的緣由。node

如下封裝的標準DSL請求方式以下:android

image

對比以後咱們發現能夠說是和gradle基本同樣,接下來就展現如何封裝。git

request2

分析

  • 首先請求是放在一個request對象內
  • request內包含多個方法loader、start、onSuccess等,做用也很明顯就是,再也不過多闡述

構建request

class Request<T> {
    lateinit var loader: suspend () -> T

    var start: (() -> Unit)? = null

    var onSuccess: ((T) -> Unit)? = null

    var onError: ((String) -> Unit)? = null

    var onComplete: (() -> Unit)? = null

    var addLifecycle: LifecycleOwner? = null

    fun request() {
        request(addLifecycle)
    }

    fun request(addLifecycle: LifecycleOwner?) {

        GlobalScope.launch(context = Dispatchers.Main) {

            start?.invoke()
            try {
                val deferred = GlobalScope.async(Dispatchers.IO, start = CoroutineStart.LAZY) {
                    loader()
                }
                addLifecycle?.apply { lifecycle.addObserver(CoroutineLifecycleListener(deferred, lifecycle)) }
                val result = deferred.await()
                onSuccess?.invoke(result)
            } catch (e: Exception) {
                e.printStackTrace()
                when (e) {
                    is UnknownHostException -> onError?.invoke("network is error!")
                    is TimeoutException -> onError?.invoke("network is error!")
                    is SocketTimeoutException -> onError?.invoke("network is error!")
                    else -> onError?.invoke("network is error!")
                }
            } finally {
                onComplete?.invoke()
            }
        }
    }
}
複製代碼

Request對象建立好了,裏面放入的參數爲方法參數,而不是實體類的參數類型,可是如今使用的時候仍是須要經過new,才能建立request對象調用其request請求方法,那如何才能直接調用request方法呢,這就須要用到kotlin的擴展函數功能:github

inline fun <T> request2(buildRequest: Request<T>.() -> Unit) {
    Request<T>().apply(buildRequest).request()
}

inline fun <T> LifecycleOwner.request2(buildRequest: Request<T>.() -> Unit) {
    Request<T>().apply(buildRequest).request(this)
}
複製代碼

加入上面兩個方法以後,咱們就能夠直接調用request方法,進行網絡請求了:api

fun doHttpRequest2() {
        request2<List<UserBean>> {
            //addLifecycle 來指定依賴的生命週期的對象
//            addLifecycle = {}

            start = {
                Log.e(TAG, "start doHttpRequest2:currentThreadName:${Thread.currentThread().name}")
            }

            loader = {
                Log.e(TAG, "request doHttpRequest2:currentThreadName:${Thread.currentThread().name}")
                RetrofitHelper.getApi().getUserInfo()
            }

            onSuccess = {
                Log.e(TAG, "onSuccess doHttpRequest2:currentThreadName:${Thread.currentThread().name}")
                Log.e(TAG, it[0].toString())
            }

            onError = {
                Log.e(TAG, "onError doHttpRequest2:currentThreadName:${Thread.currentThread().name}")
            }

            onComplete = {
                Log.e(TAG, "onComplete doHttpRequest2:currentThreadName:${Thread.currentThread().name}")
            }
        }
    }
複製代碼

加入擴展函數以後,這樣看起來基本的DSL風格已經有了,可是同gradle對比一看,發現多了一個「=」號,那接下來就想辦法去除這個「=」:網絡

優化代碼調用方式

修改request以下:app

class Request<T> {
    private lateinit var loader: suspend () -> T

    private var start: (() -> Unit)? = null

    private var onSuccess: ((T) -> Unit)? = null

    private var onError: ((String) -> Unit)? = null

    private var onComplete: (() -> Unit)? = null

    private var addLifecycle: LifecycleOwner? = null


    infix fun loader(loader: suspend () -> T){
        this.loader = loader
    }

    infix fun start(start: (() -> Unit)?){
        this.start = start
    }

    infix fun onSuccess(onSuccess: ((T) -> Unit)?){
        this.onSuccess = onSuccess
    }

    infix fun onError(onError: ((String) -> Unit)?){
        this.onError = onError
    }

    infix fun onComplete(onComplete: (() -> Unit)?){
        this.onComplete = onComplete
    }

    infix fun addLifecycle(addLifecycle: LifecycleOwner?){
        this.addLifecycle = addLifecycle
    }

    fun request() {
        request(addLifecycle)
    }

    fun request(addLifecycle: LifecycleOwner?) {

        GlobalScope.launch(context = Dispatchers.Main) {

            start?.invoke()
            try {
                val deferred = GlobalScope.async(Dispatchers.IO, start = CoroutineStart.LAZY) {
                    loader()
                }
                addLifecycle?.apply { lifecycle.addObserver(CoroutineLifecycleListener(deferred, lifecycle)) }
                val result = deferred.await()
                onSuccess?.invoke(result)
            } catch (e: Exception) {
                e.printStackTrace()
                when (e) {
                    is UnknownHostException -> onError?.invoke("network is error!")
                    is TimeoutException -> onError?.invoke("network is error!")
                    is SocketTimeoutException -> onError?.invoke("network is error!")
                    else -> onError?.invoke("network is error!")
                }
            } finally {
                onComplete?.invoke()
            }
        }
    }
}
複製代碼

之因此有「=」,是由於咱們把執行的方法看成參數傳入的,咱們將參數提供一個set方法進行賦值就能夠去除「=」,可是調用set方法會出現(),這時咱們增長infix字段修飾,這樣在set的時候能夠直接去除()替換爲{},修改以後調用方法就變成了咱們所須要的DSL風格,與gradle一模一樣:async

/**
 * 打印結果以下:
 *
 * LifecycleMainPresenter: start doHttpRequest:currentThreadName:main
 * LifecycleMainPresenter: request doHttpRequest:currentThreadName:DefaultDispatcher-worker-2
 * LifecycleMainPresenter: onSuccess doHttpRequest:currentThreadName:main
 * LifecycleMainPresenter: UserBean(login=null, id=61097549, node_id=MDEwOlJlcG9zaXRvcnk2MTA5NzU0OQ==, avatar_url=null, gravatar_id=null, url=https://api.github.com/repos/JavaNoober/Album, html_url=https://github.com/JavaNoober/Album, followers_url=null, following_url=null, gists_url=null, starred_url=null, subscriptions_url=null, organizations_url=null, repos_url=null, events_url=https://api.github.com/repos/JavaNoober/Album/events, received_events_url=null, type=null, site_admin=false, name=Album, company=null, blog=null, location=null, email=null, hireable=null, bio=null, public_repos=0, public_gists=0, followers=0, following=0, created_at=2016-06-14T06:28:05Z, updated_at=2016-06-14T06:40:26Z)
 * LifecycleMainPresenter: onComplete doHttpRequest:currentThreadName:main
 */
fun doHttpRequest2() {
    request2<List<UserBean>> {
        start {
            Log.e(TAG, "start doHttpRequest2:currentThreadName:${Thread.currentThread().name}")
        }

        loader {
            Log.e(TAG, "request doHttpRequest2:currentThreadName:${Thread.currentThread().name}")
            RetrofitHelper.getApi().getUserInfo()
        }

        onSuccess {
            Log.e(TAG, "onSuccess doHttpRequest2:currentThreadName:${Thread.currentThread().name}")
            Log.e(TAG, it[0].toString())
        }

        onError {
            Log.e(TAG, "onError doHttpRequest2:currentThreadName:${Thread.currentThread().name}")
        }

        onComplete {
            Log.e(TAG, "onComplete doHttpRequest2:currentThreadName:${Thread.currentThread().name}")
        }
    }
}
複製代碼

總結

上篇文章(DSL形式的基於retrofit、協程的網絡請求封裝)主要是介紹瞭如何去封裝協程+retrofit的網絡請求,這篇則是更加***側重於封裝DSL風格***請求,完整的代碼已上傳至github(CoroutinesHttp),歡迎你們提出更好的建議方法。

相關文章
相關標籤/搜索