kotlin 使用協程編寫高效的併發程序

一.協程基本用法

1.協程:協程允許我們在單線程模式下模擬多線程編程效果,代碼執行時的掛起與恢復完全由編程語言控制,和操作系統無關。

//GlobalScope 是頂層協議,當前應用程序結束協程跟着結束
fun main(){
    GlobalScope.launch {
        println("codes run is coroutine scope")
    }
}

//此時只會打印codes run is coroutine scope,不會打印codes run is coroutine scope finished,因爲main結束了會強制結束當前協程
fun main(){
    GlobalScope.launch {
        println("codes run is coroutine scope")
        delay(1500)
        println("codes run is coroutine scope finished")
    }
    Thread.sleep(1000)
}

//此時只會打印codes run is coroutine scope和codes run is coroutine scope finished,因爲runBlocking 會在協程內的代碼和子協程未完成的情況下阻塞線程
fun main(){
    runBlocking {
        println("codes run is coroutine scope")
        delay(1500)
        println("codes run is coroutine scope finished")
    }
    Thread.sleep(1000)
}

//打印順序launch1 launch2 launch1 finished launch2 finished
//子作用域可以併發執行,並且根據編程語言先後執行順序
fun main(){
    runBlocking {
        launch{
            println("launch1")
            delay(1000)
            println("launch1 finished")
        }
        launch{
            println("launch2")
            delay(1000)
            println("launch2 finished")
        }
    }
    Thread.sleep(1000)
}

//聲明成掛起函數,掛起函數可以相互調用的
suspend fun printDot(){
    println(".")
    delay(1000)
}

//由於suspend 只能聲明爲掛起函數,無法提供作用域
//coroutineScope 是掛起函數,並且可以繼承外部作用域創建子作用域
//coroutineScope 只會阻塞當前的協程,不會阻塞線程,runBlocking 會阻塞當前線程
suspend fun printDot() = coroutineScope {
    launch{
        println(".")
        delay(1000)
    }
}


2.更多作用域構建器

//取消當前協程
val job = GlobalScope.launch {
    //處理具體邏輯
}
job.cancel()

//GlobalScope頂層協議,多個協議需要一個一個取消不方便
//CoroutineScope可以實現一個一個取消
val job = Job()
val scope = CoroutineScope(job)
scope.launch {

}
job.cancel()

//獲取協程內容返回值
fun main(){
    runBlocking {
        val result = async {
            5+5
        }.await()//後面的代碼會在有返回值後才執行,所以有多個async請在最後執行await,不然會串行執行,效率很低
        println(result)
    }
}

//相當於上面代碼
fun main(){
    runBlocking {
        val result = withContext(Dispatchers.Default){
            5+5
        }
        println(result)
    }
}



3.使用協程簡化回調的寫法

val appService = ServiceCreator.create<AppService>()
appService.getAppData().enqueue(object:Callback<List<App>>) {
    
    override fun onResponse(call:Callback<List<App>>,response:Response<List<App>>) {
        //得到服務器返回的數據
    }

    override fun onFailure(call:Callback<List<App>>,t:Throwble) {
        //對異常進行處理
    }

}

//suspendCoroutine函數可以簡化上面的回調
//suspendCoroutine必須在協程作用域或者掛起函數中調用,他接受lambda表達式
suspend fun <T> Call<T>.await():T{
    return suspendCoroutine { continuation ->
        enqueue(object:Callback<List<App>>) {
    
            override fun onResponse(call:Callback<List<App>>,response:Response<List<App>>) {
                val body = response.body()
                if(body!=null)
                    continuation.resume(body)
                else
                    continuation.resumeWithException(RuntimeExceotion("respnse body is null")) 
            }

            override fun onFailure(call:Callback<List<App>>,t:Throwble) {
                //對異常進行處理
                continuation.resumeWithException(t) 
            }
    }
}

suspend fun getAppData(){
    try {
        val appList = ServiceCreator.creat<AppService>().getAppData().await()
        //對服務器響應數據進行處理
    } catch (e:Exception) {
        //對異常做處理
    }
}

//上述掛起函數可以在LiveData中調用,因爲LiveData的代碼塊中提供一個掛起函數的上下文
object Respository {

    fun searchPlaces(query:String) = liveData(Dispatchers.IO){
        val result = try{
            val placeResponse = SunnyWeatherNetwork.searchPlaces(query)
            if(placeResponse.status == "ok"){
                val places = placeResponse.places
                Result,success(places)
            }else{
                Result.failure(RuntimeExceotion("response status is ${placeResponse.status}"))
            }
        }catch(e:Exception){
            Result.failure<List<Place>>(e)
        }
        emit(result)
    }    

}

 

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