美文网首页
我的Kotlin 学习之路(六)Kotlin之coroutine

我的Kotlin 学习之路(六)Kotlin之coroutine

作者: 活着工作室 | 来源:发表于2017-06-27 13:43 被阅读0次

    Coroutine -> 协程
    不同于线程,协程不占用CPU,它只占用内存来处理耗时操作。Coroutine的原理有大牛的视频已经介绍了,我就不搬门弄斧了,这一章主要讲讲Kotlinx.coroutines 这个库的使用方法,有关于这个的中文讲解还不是很多!

    我们依旧使用官方文档中加载google天气的例子来代替耗时操作

    一、导入框架
    compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.16'

    二、Request类

    /**
     * 网络请求类
     * Created by zr on 2017/5/23.
     */
    class Request (val url:String){
    
        fun run():String{
            Log.e(javaClass.simpleName,"*******************************************")
            Log.e(javaClass.simpleName,"url = "+url)
    
            var forecastJsonUrl = URL(url).readText()
    
            Log.e(javaClass.simpleName,"json = "+forecastJsonUrl)
            return forecastJsonUrl
        }
    }
    
    

    三、上最简单的协程

    
    fun coroution(){
            launch(CommonPool){ //协程
                Request(url).run() //耗时操作在CommonPool线程池中
            }
            Log.e(TAG, "开始请求")
        }
    
    
    结果
    06-27 13:01:58.928 16919-16919/test.futsal.com.mykotlin E/MainActivity: 开始请求
    06-27 13:01:58.928 16919-19406/test.futsal.com.mykotlin E/Request: *******************************************
    06-27 13:01:58.928 16919-19406/test.futsal.com.mykotlin E/Request: url = http://api.openweathermap.org/data/2.5/forecast/daily?mode=json&units=metric&cnt=7&APPID=15646a06818f61f7b8d7823ca833e1ce&id=2038349
    06-27 13:01:59.936 16919-19406/test.futsal.com.mykotlin E/Request: json = {"city":{"id":2038349,"name":"Beijing Shi","coord":{"lon":116.3971,"lat":39.9169},"country":"CN","population":0},"cod":"200","message":10.1232268,"cnt":7,"list":[{"dt":1498536000,"temp":{"day":31,"min":23.42,"max":33.85,"night":23.42,"eve":33.61,"morn":31},"pressure":996.6,"humidity":64,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":1.83,"deg":141,"clouds":0},{"dt":1498622400,"temp":{"day":30.6,"min":19.7,"max":33.27,"night":26.23,"eve":32.46,"morn":19.7},"pressure":996.96,"humidity":61,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":2.06,"deg":172,"clouds":0},{"dt":1498708800,"temp":{"day":30.33,"min":21.67,"max":33.34,"night":23.4,"eve":32.87,"morn":21.67},"pressure":997.1,"humidity":62,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"speed":1.63,"deg":146,"clouds":48},{"dt":1498795200,"temp":{"day":32.12,"min":19.89,"max":32.12,"night":19.89,"eve":26.48,"morn":25.23},"pressure":966.77,"humidity":0,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":1.62,"deg":178,"clouds":27},{"dt":1498881600,"temp":{"day":31.48,"min":19.94,"max":31.48,"night":19.94,"eve":26.16,"morn":24.87},"pressure":966.47,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.54,"deg":167,"clouds":43},{"dt":1498968000,"temp":{"day":32.06,"min":20.86,"max":32.06,"night":20.86,"eve":27.1,"morn":25.03},"pressure":965.1,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.05,"deg":119,"clouds":13,"rain":0.38},{"dt":1499054400,"temp":{"day":29.36,"min":21.53,"max":29.36,"night":21.53,"eve":25.73,"morn":24.72},"pressure":965.4,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.35,"deg":136,"clouds":13,"rain":2.03}]}
    
    
    
    

    CommonPool 是 Coroutine的分发类的对象

    /*
     * Copyright 2016-2017 JetBrains s.r.o.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     * http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package kotlinx.coroutines.experimental
    
    import java.util.concurrent.ExecutorService
    import java.util.concurrent.Executors
    import java.util.concurrent.TimeUnit
    import java.util.concurrent.atomic.AtomicInteger
    import kotlin.coroutines.experimental.CoroutineContext
    
    /**
     * Represents common pool of shared threads as coroutine dispatcher for compute-intensive tasks.
     * It uses [java.util.concurrent.ForkJoinPool] when available, which implements efficient work-stealing algorithm for its queues, so every
     * coroutine resumption is dispatched as a separate task even when it already executes inside the pool.
     * When available, it wraps `ForkJoinPool.commonPool` and provides a similar shared pool where not.
     */
    object CommonPool : CoroutineDispatcher() {
        private var usePrivatePool = false
    
        @Volatile
        private var _pool: ExecutorService? = null
    
        private inline fun <T> Try(block: () -> T) = try { block() } catch (e: Throwable) { null }
    
        private fun createPool(): ExecutorService {
            val fjpClass = Try { Class.forName("java.util.concurrent.ForkJoinPool") }
                ?: return createPlainPool()
            if (!usePrivatePool) {
                Try { fjpClass.getMethod("commonPool")?.invoke(null) as? ExecutorService }
                    ?.let { return it }
            }
            Try { fjpClass.getConstructor(Int::class.java).newInstance(defaultParallelism()) as? ExecutorService }
                ?. let { return it }
            return createPlainPool()
        }
    
        private fun createPlainPool(): ExecutorService {
            val threadId = AtomicInteger()
            return Executors.newFixedThreadPool(defaultParallelism()) {
                Thread(it, "CommonPool-worker-${threadId.incrementAndGet()}").apply { isDaemon = true }
            }
        }
    
        private fun defaultParallelism() = (Runtime.getRuntime().availableProcessors() - 1).coerceAtLeast(1)
    
        @Synchronized
        private fun getOrCreatePoolSync(): ExecutorService =
            _pool ?: createPool().also { _pool = it }
    
        override fun dispatch(context: CoroutineContext, block: Runnable) =
            (_pool ?: getOrCreatePoolSync()).execute(block)
    
        // used for tests
        @Synchronized
        internal fun usePrivatePool() {
            shutdownAndRelease(0)
            usePrivatePool = true
        }
    
        // used for tests
        @Synchronized
        internal fun shutdownAndRelease(timeout: Long) {
            _pool?.apply {
                shutdown()
                if (timeout > 0)
                    awaitTermination(timeout, TimeUnit.MILLISECONDS)
                _pool = null
            }
            usePrivatePool = false
        }
    
        override fun toString(): String = "CommonPool"
    }
    
    

    四、在主线程中操作

    fun coroution() = runBlocking<Unit> { //runBlocking代表在主线程中
            launch(CommonPool){
                Request(url).run()
            }
            Log.e(TAG, "开始请求")
        }
    
    

    五、带返回值的协程

    fun coroution()= runBlocking<Unit> {
            val job = async(CommonPool){ //async 替代了launch (defer已过时)
                Request(url).run()
            }
            job.join() // async()结束后才进行下面的代码,否则挂起等待
            
            var result:String = job.await() //await()获得async方法的返回值
            Log.e(TAG,"result = $result")
    
            result?.let {
                var forecastResult: ResponseClasses.ForecastResult
                forecastResult = Gson().fromJson(result, ResponseClasses.ForecastResult::class.java)
                longToast("成功")
                toolbar.title = forecastResult.city.name
                tv_main.text = ""
            }
    
        }
    
    
    结果
    06-27 13:17:19.214 21646-22256/test.futsal.com.mykotlin E/Request: *******************************************
    06-27 13:17:19.215 21646-22256/test.futsal.com.mykotlin E/Request: url = http://api.openweathermap.org/data/2.5/forecast/daily?mode=json&units=metric&cnt=7&APPID=15646a06818f61f7b8d7823ca833e1ce&id=2038349
    06-27 13:17:20.213 21646-22256/test.futsal.com.mykotlin E/Request: json = {"city":{"id":2038349,"name":"Beijing Shi","coord":{"lon":116.3971,"lat":39.9169},"country":"CN","population":0},"cod":"200","message":11.058764,"cnt":7,"list":[{"dt":1498536000,"temp":{"day":31,"min":23.42,"max":33.85,"night":23.42,"eve":33.61,"morn":31},"pressure":996.6,"humidity":64,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":1.83,"deg":141,"clouds":0},{"dt":1498622400,"temp":{"day":30.6,"min":19.7,"max":33.27,"night":26.23,"eve":32.46,"morn":19.7},"pressure":996.96,"humidity":61,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":2.06,"deg":172,"clouds":0},{"dt":1498708800,"temp":{"day":30.33,"min":21.67,"max":33.34,"night":23.4,"eve":32.87,"morn":21.67},"pressure":997.1,"humidity":62,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"speed":1.63,"deg":146,"clouds":48},{"dt":1498795200,"temp":{"day":32.12,"min":19.89,"max":32.12,"night":19.89,"eve":26.48,"morn":25.23},"pressure":966.77,"humidity":0,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":1.62,"deg":178,"clouds":27},{"dt":1498881600,"temp":{"day":31.48,"min":19.94,"max":31.48,"night":19.94,"eve":26.16,"morn":24.87},"pressure":966.47,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.54,"deg":167,"clouds":43},{"dt":1498968000,"temp":{"day":32.06,"min":20.86,"max":32.06,"night":20.86,"eve":27.1,"morn":25.03},"pressure":965.1,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.05,"deg":119,"clouds":13,"rain":0.38},{"dt":1499054400,"temp":{"day":29.36,"min":21.53,"max":29.36,"night":21.53,"eve":25.73,"morn":24.72},"pressure":965.4,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.35,"deg":136,"clouds":13,"rain":2.03}]}
    06-27 13:17:20.214 21646-21646/test.futsal.com.mykotlin E/MainActivity: result = {"city":{"id":2038349,"name":"Beijing Shi","coord":{"lon":116.3971,"lat":39.9169},"country":"CN","population":0},"cod":"200","message":11.058764,"cnt":7,"list":[{"dt":1498536000,"temp":{"day":31,"min":23.42,"max":33.85,"night":23.42,"eve":33.61,"morn":31},"pressure":996.6,"humidity":64,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":1.83,"deg":141,"clouds":0},{"dt":1498622400,"temp":{"day":30.6,"min":19.7,"max":33.27,"night":26.23,"eve":32.46,"morn":19.7},"pressure":996.96,"humidity":61,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":2.06,"deg":172,"clouds":0},{"dt":1498708800,"temp":{"day":30.33,"min":21.67,"max":33.34,"night":23.4,"eve":32.87,"morn":21.67},"pressure":997.1,"humidity":62,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"speed":1.63,"deg":146,"clouds":48},{"dt":1498795200,"temp":{"day":32.12,"min":19.89,"max":32.12,"night":19.89,"eve":26.48,"morn":25.23},"pressure":966.77,"humidity":0,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":1.62,"deg":178,"clouds":27},{"dt":1498881600,"temp":{"day":31.48,"min":19.94,"max":31.48,"night":19.94,"eve":26.16,"morn":24.87},"pressure":966.47,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.54,"deg":167,"clouds":43},{"dt":1498968000,"temp":{"day":32.06,"min":20.86,"max":32.06,"night":20.86,"eve":27.1,"morn":25.03},"pressure":965.1,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.05,"deg":119,"clouds":13,"rain":0.38},{"dt":1499054400,"temp":{"day":29.36,"min":21.53,"max":29.36,"night":21.53,"eve":25.73,"morn":24.72},"pressure":965.4,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.35,"deg":136,"clouds":13,"rain":2.03}]}
    
    
    
    

    六、取消

    fun coroution()= runBlocking<Unit> {
            var result:String? = null
            val job = launch(CommonPool){
                result = Request(url).run()
            }
        job.cancel(Exception("Activity is onPause")) //取消并告知原因
        job.join()
        Log.e(TAG,"result = $result")
            result?.let {
                var forecastResult: ResponseClasses.ForecastResult
                forecastResult = Gson().fromJson(result, ResponseClasses.ForecastResult::class.java)
                longToast("成功")
                toolbar.title = forecastResult.city.name
                
                tv_main.text = ""
            }
        }
    
    

    取消后的异常

    QQ图片20170627101735.png

    七、async(CommonPool)的构造方法

    /**
     * Creates new coroutine and returns its future result as an implementation of [Deferred].
     *
     * The running coroutine is cancelled when the resulting object is [cancelled][Job.cancel].
     * The [context] for the new coroutine must be explicitly specified.
     * See [CoroutineDispatcher] for the standard [context] implementations that are provided by `kotlinx.coroutines`.
     * The [context][CoroutineScope.context] of the parent coroutine from its [scope][CoroutineScope] may be used,
     * in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
     *
     * By default, the coroutine is immediately scheduled for execution.
     * Other options can be specified via `start` parameter. See [CoroutineStart] for details.
     * An optional [start] parameter can be set to [CoroutineStart.LAZY] to start coroutine _lazily_. In this case,,
     * the resulting [Deferred] is created in _new_ state. It can be explicitly started with [start][Job.start]
     * function and will be started implicitly on the first invocation of [join][Job.join] or [await][Deferred.await].
     *
     * @param context context of the coroutine
     * @param start coroutine start option
     * @param block the coroutine code
     */
    public fun <T> async(
        context: CoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend CoroutineScope.() -> T
    ): Deferred<T> {
        val newContext = newCoroutineContext(context)
        val coroutine = if (start.isLazy)
            LazyDeferredCoroutine(newContext, block) else
            DeferredCoroutine<T>(newContext, active = true)
        coroutine.initParentJob(context[Job])
        start(block, coroutine, coroutine)
        return coroutine
    }
    
    

    试试 第二个参数 start: CoroutineStart
    async(CommonPool, CoroutineStart.LAZY)
    async(CommonPool, CoroutineStart.ATOMIC)
    async(CommonPool, CoroutineStart.UNDISPATCHED)
    再看CoroutineStart类

    /*
     * Copyright 2016-2017 JetBrains s.r.o.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     * http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package kotlinx.coroutines.experimental
    
    import kotlinx.coroutines.experimental.CoroutineStart.*
    import kotlinx.coroutines.experimental.intrinsics.startCoroutineUndispatched
    import kotlin.coroutines.experimental.Continuation
    import kotlin.coroutines.experimental.startCoroutine
    
    /**
     * Defines start option for coroutines builders.
     * It is used in `start` parameter of [launch], [async], and [actor][kotlinx.coroutines.experimental.channels.actor]
     * coroutine builder functions.
     *
     * The summary of coroutine start options is:
     * * [DEFAULT] -- immediately schedules coroutine for execution according to its context;
     * * [LAZY] -- starts coroutine lazily, only when it is needed;
     * * [ATOMIC] -- atomically (non-cancellably) schedules coroutine for execution according to its context;
     * * [UNDISPATCHED] -- immediately executes coroutine until its first suspension point _in the current thread_.
     */
    public enum class CoroutineStart {
        /**
         * Default -- immediately schedules coroutine for execution according to its context.
         *
         * If the [CoroutineDispatcher] of the coroutine context returns `true` from [CoroutineDispatcher.isDispatchNeeded]
         * function as most dispatchers do, then the coroutine code is dispatched for execution later, while the code that
         * invoked the coroutine builder continues execution.
         *
         * Note, that [Unconfined] dispatcher always returns `false` from its [CoroutineDispatcher.isDispatchNeeded]
         * function, so starting coroutine with [Unconfined] dispatcher by [DEFAULT] is the same as using [UNDISPATCHED].
         *
         * If coroutine [Job] is cancelled before it even had a chance to start executing, then it will not start its
         * execution at all, but complete with an exception.
         *
         * Cancellability of coroutine at suspension points depends on the particular implementation details of
         * suspending functions. Use [suspendCancellableCoroutine] to implement cancellable suspending functions.
         */
        DEFAULT,
    
        /**
         * Starts coroutine lazily, only when it is needed.
         *
         * See the documentation for the corresponding coroutine builders for details:
         * [launch], [async], and [actor][kotlinx.coroutines.experimental.channels.actor].
         *
         * If coroutine [Job] is cancelled before it even had a chance to start executing, then it will not start its
         * execution at all, but complete with an exception.
         */
        LAZY,
    
        /**
         * Atomically (non-cancellably) schedules coroutine for execution according to its context.
         * This is similar to [DEFAULT], but the coroutine cannot be cancelled before it starts executing.
         *
         * Cancellability of coroutine at suspension points depends on the particular implementation details of
         * suspending functions as in [DEFAULT].
         */
        ATOMIC,
    
        /**
         * Immediately executes coroutine until its first suspension point _in the current thread_ as if it the
         * coroutine was started using [Unconfined] dispatcher. However, when coroutine is resumed from suspension
         * it is dispatched according to the [CoroutineDispatcher] in its context.
         *
         * This is similar to [ATOMIC] in the sense that coroutine starts executing even if it was already cancelled,
         * but the difference is that it start executing in the same thread.
         *
         * Cancellability of coroutine at suspension points depends on the particular implementation details of
         * suspending functions as in [DEFAULT].
         */
        UNDISPATCHED;
    
        /**
         * Starts the corresponding block as a coroutine with this coroutine start strategy.
         *
         * * [DEFAULT] uses [startCoroutineCancellable].
         * * [ATOMIC] uses [startCoroutine].
         * * [UNDISPATCHED] uses [startCoroutineUndispatched].
         * * [LAZY] does nothing.
         */
        public operator fun <T> invoke(block: suspend () -> T, completion: Continuation<T>) =
            when (this) {
                CoroutineStart.DEFAULT -> block.startCoroutineCancellable(completion)
                CoroutineStart.ATOMIC -> block.startCoroutine(completion)
                CoroutineStart.UNDISPATCHED -> block.startCoroutineUndispatched(completion)
                CoroutineStart.LAZY -> Unit // will start lazily
            }
    
        /**
         * Starts the corresponding block with receiver as a coroutine with this coroutine start strategy.
         *
         * * [DEFAULT] uses [startCoroutineCancellable].
         * * [ATOMIC] uses [startCoroutine].
         * * [UNDISPATCHED] uses [startCoroutineUndispatched].
         * * [LAZY] does nothing.
         */
        public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>) =
            when (this) {
                CoroutineStart.DEFAULT -> block.startCoroutineCancellable(receiver, completion)
                CoroutineStart.ATOMIC -> block.startCoroutine(receiver, completion)
                CoroutineStart.UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
                CoroutineStart.LAZY -> Unit // will start lazily
            }
    
        /**
         * Returns `true` when [LAZY].
         */
        public val isLazy: Boolean get() = this === LAZY
    }
    
    
    

    (我英语不好,成人三级一直没过,请包涵,但我不会瞎说的)
    DEFAULT -> 立即执行耗时操作
    LAZY -> 在async 的 await()时才进行耗时操作 Starts coroutine lazily, only when it is needed.
    ATOMIC -> 和DEFAULT很接近,但是不能取消 (non-cancellably)
    UNDISPATCHED-> 只在当前线程执行,不切换线程,如果是在主线程,刚才的方法会抛出这样的异常

    QQ图片20170627105748.png

    我最后的代码

    val url = "http://api.openweathermap.org/data/2.5/forecast/daily?mode=json&units=metric&cnt=7&APPID=15646a06818f61f7b8d7823ca833e1ce&id=2038349"
    
    fun coroution()= runBlocking<Unit> {
            val result = async(CommonPool){
                Request(url).run()
            }.await()
    
            Log.e(TAG,"result = $result")
            result?.let {
                var forecastResult: ResponseClasses.ForecastResult
                forecastResult = Gson().fromJson(result, ResponseClasses.ForecastResult::class.java)
                longToast("成功")
                toolbar.title = forecastResult.city.name
                tv_main.text = ""
                //加载adapter的略过
            }
    
        }
    

    最后的样子

    QQ图片20170627123059.png

    相关文章

      网友评论

          本文标题:我的Kotlin 学习之路(六)Kotlin之coroutine

          本文链接:https://www.haomeiwen.com/subject/xffecxtx.html