一.協程基本用法
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)
}
}