Like using gradle, network requests are made in kotlin

Keywords: network Gradle github Android

Preface

DSL is the abbreviation of Domain-Specific Language (DSL). Wikipedia is defined as a computer language focused on an application domain.
This statement seems abstract. In fact, the commonly used gradle is the most common manifestation of DSL. Let's take a look at the build.gradle in android project:

 

Android {} and dependencies {} are the manifestations of DSL, which are more concise and more obvious than the traditional writing, such as the configuration file-like method of execution, which is why DSL writing is recommended.

The standard DSL requests encapsulated below are as follows:

 

 

After comparison, we find that it's basically the same as gradle. Next, we show how to encapsulate it.

request2

Analysis

  • The first request is placed in a request object
  • request contains multiple methods such as loader, start, onSuccess, and so on. It's also obvious that it doesn't elaborate too much.

Building 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 object is created. The parameters put in it are method parameters, not entity class parameter types. But now we need to use new to create request object to call its request request request method. How can we call request method directly? This requires the use of kotlin's extension function. Can:

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)
}

After adding the above two methods, we can call the request method directly and make network requests.

    fun doHttpRequest2() {
        request2<List<UserBean>> {
            //addLifecycle to specify dependent lifecycle objects
//            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}")
            }
        }
    }

After adding the extension function, it seems that the basic DSL style is already there, but compared with gradle, we find that there is an additional "=" sign, then we will try to remove this "=":

Optimizing code invocation

Modify the request as follows:

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()
            }
        }
    }
}

The reason why there is "=" is that we pass in the method we execute as a parameter, we can remove "=" by providing a set method to assign the parameter, but call the set method will appear (), then we add infix field modification, so that when set, we can directly remove () and replace {} with {} and then modify the caller. Law becomes the DSL style we need, just like gradle:

/**
 * The printing results are as follows:
 *
 * 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}")
        }
    }
}

 

Last

If you see it here and think it's a good article, give it a compliment! _____________ Welcome to comment and discuss! If you think it's worth improving, please leave me a message. We will make serious inquiries, correct shortcomings, and regularly share technical dry goods free of charge. Thank you!

Posted by maqmus on Sat, 10 Aug 2019 06:59:51 -0700