【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("協程之後")
    }


}


效果不變

 

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