【40】kotlin 协程 异步下载图片3

改为异步 新建async包 

新建AsyncTask.kt

package com.yzdzy.kotlin.chapter7.async

import java.util.concurrent.Executors

//线程池
private val pool by lazy {
    Executors.newCachedThreadPool()
}
class AsyncTask(val block: () -> Unit) {
    fun execute() = pool.execute(block)
}

修改BaseCoroutines.kt

package com.yzdzy.kotlin.chapter7.basic

import cn.kotliner.coroutine.common.HttpError
import cn.kotliner.coroutine.common.HttpException
import cn.kotliner.coroutine.common.HttpService
import cn.kotliner.coroutine.common.log
import cn.kotliner.coroutine.ui.LOGO_URL
import com.yzdzy.kotlin.chapter7.async.AsyncTask
import java.lang.Exception
import javax.xml.ws.http.HTTPException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.startCoroutine
import kotlin.coroutines.suspendCoroutine

fun 我要开始协程了(block: suspend () -> Unit) {
    block.startCoroutine(BaseCoroutines())
}

suspend fun 我要开始加载图片了(url: String) = suspendCoroutine<ByteArray> { continuation ->

    AsyncTask {

        log("耗时下载图片")
        try {
            val reponseBody = HttpService.service.getLogo(LOGO_URL).execute()
            if (reponseBody.isSuccessful) {
                reponseBody.body()?.byteStream()?.readBytes()?.let(continuation::resume)
            } else {
                continuation.resumeWithException(HttpException(reponseBody.code()))
            }
        } catch (e: Exception) {
            continuation.resumeWithException(e)
        }
    }.execute()
}

运行看log

异步已经完成了。

但是main setLogo应该在主线程执行。而不是子线程。。如何操作呢

 继续修改BaseCoroutines.kt 下面的第一段代码是讲如何切换到ui线程 第二段是整个BaseCoroutines.kt

      //切换ui线程
                SwingUtilities.invokeLater{
                    continuation.resumeWithException(HttpException(reponseBody.code()))
                }
package com.yzdzy.kotlin.chapter7.basic

import cn.kotliner.coroutine.common.HttpError
import cn.kotliner.coroutine.common.HttpException
import cn.kotliner.coroutine.common.HttpService
import cn.kotliner.coroutine.common.log
import cn.kotliner.coroutine.ui.LOGO_URL
import com.yzdzy.kotlin.chapter7.async.AsyncTask
import java.lang.Exception
import javax.swing.SwingUtilities
import javax.xml.ws.http.HTTPException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.startCoroutine
import kotlin.coroutines.suspendCoroutine

fun 我要开始协程了(block: suspend () -> Unit) {
    block.startCoroutine(BaseCoroutines())
}

suspend fun 我要开始加载图片了(url: String) = suspendCoroutine<ByteArray> { continuation ->

    AsyncTask {

        log("耗时下载图片")
        try {
            val reponseBody = HttpService.service.getLogo(LOGO_URL).execute()
            if (reponseBody.isSuccessful) {
                reponseBody.body()?.byteStream()?.readBytes()?.let{
                    //切换ui线程
                    SwingUtilities.invokeLater{
                        continuation.resume(it)
                    }
                }

            } else {
                //切换ui线程
                SwingUtilities.invokeLater{
                    continuation.resumeWithException(HttpException(reponseBody.code()))
                }

            }
        } catch (e: Exception) {
            SwingUtilities.invokeLater{
                continuation.resumeWithException(e)
            }

        }
    }.execute()
}

可以看到上面用了三次的

SwingUtilities.invokeLater{

为了简化

继续在 async 包中新建 UiCotinuationWrapper.kt

package com.yzdzy.kotlin.chapter7.async

import javax.swing.SwingUtilities
import kotlin.coroutines.Continuation

/**
 * Created by benny on 5/29/17.
 */
class UiCotinuationWrapper<T>(val continuation: Continuation<T>): Continuation<T>{
    override val context = continuation.context

    override fun resumeWith(result: Result<T>) {
        SwingUtilities.invokeLater { continuation.resumeWith(result) }
    }

}

BaseCoroutines.kt 就可以写成

package com.yzdzy.kotlin.chapter7.basic

import cn.kotliner.coroutine.common.HttpException
import cn.kotliner.coroutine.common.HttpService
import cn.kotliner.coroutine.common.log
import cn.kotliner.coroutine.ui.LOGO_URL
import com.yzdzy.kotlin.chapter7.async.AsyncTask
import com.yzdzy.kotlin.chapter7.async.UiCotinuationWrapper
import java.lang.Exception
import javax.swing.SwingUtilities
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.startCoroutine
import kotlin.coroutines.suspendCoroutine

fun 我要开始协程了(block: suspend () -> Unit) {
    block.startCoroutine(BaseCoroutines())
}

suspend fun 我要开始加载图片了(url: String) = suspendCoroutine<ByteArray> { continuation ->
    AsyncTask {
        val uiCotinuationWrapper = UiCotinuationWrapper(continuation)
        log("耗时下载图片")
        try {
            val reponseBody = HttpService.service.getLogo(LOGO_URL).execute()
            if (reponseBody.isSuccessful) {
                reponseBody.body()?.byteStream()?.readBytes()?.let {
                    //切换ui线程

                    uiCotinuationWrapper.resume(it)

                }

            } else {
                //切换ui线程

                uiCotinuationWrapper.resumeWithException(HttpException(reponseBody.code()))


            }
        } catch (e: Exception) {

            uiCotinuationWrapper.resumeWithException(e)


        }
    }.execute()
}

运行下效果不变

但是仍然定义了  val uiCotinuationWrapper = UiCotinuationWrapper(continuation)

然后再使用 uiCotinuationWrapper 调用其他方法 感觉仍然不方便

啊哦 上面log错了一个地方 这个地方改一下

package cn.kotliner.coroutine.ui


import cn.kotliner.coroutine.common.HttpError
import cn.kotliner.coroutine.common.HttpService
import cn.kotliner.coroutine.common.log
import com.yzdzy.kotlin.chapter7.async.AsyncTask
import com.yzdzy.kotlin.chapter7.basic.我要开始加载图片了
import com.yzdzy.kotlin.chapter7.basic.我要开始协程了
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.Callback
import retrofit2.HttpException
import retrofit2.Response
import javax.swing.JFrame.EXIT_ON_CLOSE
import javax.swing.SwingUtilities

/**
 * Created by benny on 5/20/17.
 */
const val LOGO_URL = "http://www.imooc.com/static/img/index/logo.png?t=1.1"

fun main(args: Array<String>) {
    val frame = MainWindow()
    frame.title = "Coroutine@Bennyhuo"
    frame.setSize(200, 150)
    frame.isResizable = true
    frame.defaultCloseOperation = EXIT_ON_CLOSE
    frame.init()
    frame.isVisible = true

    frame.onButtonClick {

        log("协程之前")
        我要开始协程了 {
            log("协程开始")
            val imageDate = 我要开始加载图片了(LOGO_URL)
            log("拿到图片")
            frame.setLogo(imageDate)
        }
        log("协程之后")
    }


}


就是

 log("协程之后")
    的位置错了

继续刚才不方便的问题。这次通过拦截器实现

在async包中新建 AsyncContext.kt

 

package com.yzdzy.kotlin.chapter7.async

import kotlin.coroutines.AbstractCoroutineContextElement
import kotlin.coroutines.Continuation
import kotlin.coroutines.ContinuationInterceptor

class AsyncContext :AbstractCoroutineContextElement(ContinuationInterceptor),ContinuationInterceptor{
    override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> {
        return UiCotinuationWrapper(continuation.context.fold(continuation){
            continuation, element ->
            if(element!=this&&element is ContinuationInterceptor){
                element.interceptContinuation(continuation)
            }else continuation
        })
    }

}

修改Corutines.kt

package com.yzdzy.kotlin.chapter7.basic

import kotlin.coroutines.Continuation
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext


class BaseCoroutines(val content: CoroutineContext = EmptyCoroutineContext) : Continuation<Unit> {
    override val context: CoroutineContext
        get() = EmptyCoroutineContext

    override fun resumeWith(result: Result<Unit>) {
    }

}

修改BaseCoroutines.kt

package com.yzdzy.kotlin.chapter7.basic

import cn.kotliner.coroutine.common.HttpException
import cn.kotliner.coroutine.common.HttpService
import cn.kotliner.coroutine.common.log
import cn.kotliner.coroutine.ui.LOGO_URL
import com.yzdzy.kotlin.chapter7.async.AsyncContext
import com.yzdzy.kotlin.chapter7.async.AsyncTask
import com.yzdzy.kotlin.chapter7.async.UiCotinuationWrapper
import java.lang.Exception
import javax.swing.SwingUtilities
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.startCoroutine
import kotlin.coroutines.suspendCoroutine

fun 我要开始协程了(block: suspend () -> Unit) {
    block.startCoroutine(BaseCoroutines(AsyncContext() ))
}

suspend fun 我要开始加载图片了(url: String) = suspendCoroutine<ByteArray> { continuation ->
    AsyncTask {
        log("耗时下载图片")
        try {
            val reponseBody = HttpService.service.getLogo(LOGO_URL).execute()
            if (reponseBody.isSuccessful) {
                reponseBody.body()?.byteStream()?.readBytes()?.let {
                    //切换ui线程

                    continuation.resume(it)

                }

            } else {
                //切换ui线程

                continuation.resumeWithException(HttpException(reponseBody.code()))


            }
        } catch (e: Exception) {

            continuation.resumeWithException(e)


        }
    }.execute()
}

 

运行看效果

完美

还可以接续优化 把异步部分提取处理啊

修改BaseCoroutines.kt

package com.yzdzy.kotlin.chapter7.basic

import cn.kotliner.coroutine.common.HttpError
import cn.kotliner.coroutine.common.HttpException
import cn.kotliner.coroutine.common.HttpService
import cn.kotliner.coroutine.common.log
import cn.kotliner.coroutine.ui.LOGO_URL
import com.yzdzy.kotlin.chapter7.async.AsyncContext
import com.yzdzy.kotlin.chapter7.async.AsyncTask
import com.yzdzy.kotlin.chapter7.async.UiCotinuationWrapper
import java.lang.Exception
import javax.swing.SwingUtilities
import kotlin.coroutines.*

fun 我要开始协程了(block: suspend () -> Unit) {
    block.startCoroutine(BaseCoroutines(AsyncContext()))
}

suspend fun <T> 我要开始耗时操作了(block: () -> T) = suspendCoroutine<T> { continuation ->
    AsyncTask {
        log("异步操作开始前")
        try {
            continuation.resume(block())
        } catch (e: Exception) {
            continuation.resumeWithException(e)
        }
    }.execute()
}


fun 我要开始加载图片了(url: String): ByteArray {
    log("下载图片开始")
    val reponseBody = HttpService.service.getLogo(url).execute()
    if (reponseBody.isSuccessful) {
        reponseBody.body()?.byteStream()?.readBytes()?.let {
            return it
        }
        throw HttpException(HttpError.HTTP_ERROR_NO_DATA)
    } else {
        //切换ui线程
        throw HttpException(reponseBody.code())
    }

}

修改main

package cn.kotliner.coroutine.ui


import cn.kotliner.coroutine.common.HttpError
import cn.kotliner.coroutine.common.HttpService
import cn.kotliner.coroutine.common.log
import com.yzdzy.kotlin.chapter7.async.AsyncTask
import com.yzdzy.kotlin.chapter7.basic.我要开始加载图片了
import com.yzdzy.kotlin.chapter7.basic.我要开始协程了
import com.yzdzy.kotlin.chapter7.basic.我要开始耗时操作了
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.Callback
import retrofit2.HttpException
import retrofit2.Response
import javax.swing.JFrame.EXIT_ON_CLOSE
import javax.swing.SwingUtilities

/**
 * Created by benny on 5/20/17.
 */
const val LOGO_URL = "http://www.imooc.com/static/img/index/logo.png?t=1.1"

fun main(args: Array<String>) {
    val frame = MainWindow()
    frame.title = "Coroutine@Bennyhuo"
    frame.setSize(200, 150)
    frame.isResizable = true
    frame.defaultCloseOperation = EXIT_ON_CLOSE
    frame.init()
    frame.isVisible = true

    frame.onButtonClick {

        log("协程之前")
        我要开始协程了 {
            log("协程开始")
            val imageDate = 我要开始耗时操作了 {
                我要开始加载图片了(LOGO_URL)
            }
            log("拿到图片")
            frame.setLogo(imageDate)
        }
        log("协程之后")
    }


}


效果不变

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章