Coroutine 協程
再強調一次
協程就是由kotlin官方所提供的線程api
//Thread Thread { }.start() //Executor val executor = Executors.newCachedThreadPool() executor.execute { } //coroutine launch { }
Rxjava都有了 coroutine的優勢在哪?
本質上跟其他的線程api一樣,方便
他借助了kotlin的語言優勢,所以他基於那些Java之上的方案會更方便一點
最重要的是
他可以用看起來同步的方式,寫出異步代碼
val user = api.getUser()//後台線程 tvName.text = user.name //主線程
這就是kotlin最有名的『非阻塞式掛起』suspend
Coroutine最大的好處,是你可以把不同線程的代碼寫在同一塊代碼塊裡面
coroutine:
launch(Dispatchers.Main) { // (主線程) val result = api.fetchData() // 異步操作,比如獲取網絡數據(後台線程) tvName.text = result.name // 在這裡處理結果,如更新UI(主線程) //上下兩行代碼,可以先切走在切回來,這是java完全做不到的事情 }正常callback寫法:
api.getUser() .enqueue(object:Callback<User>{ ... override fun onResponse(call:Call<User>, response:Response<User>){ runOnUiThread{ tvName.text = response.body().name } } })
Callback的寫法我們早就爛到骨頭裡了,你為什麼還是要學習Coroutine?
省了超多callback代碼,超簡潔
省了超多callback代碼,超簡潔
消除了callback 多線程的操作難度直接抹平了
不是10 -> 9
而是 1 -> 0
我舉一個差異最大的例子
像是我要做一個
兩次網路請求
並把這兩個輸出結果拿去做第三個網路請求
callback式的開發,要做這種工作非常困難
於是我們可能會選擇妥協
變成完成a後再call b的先後請求,這就是標準的垃圾代碼了
明明可以並行的兩個請求,由於我自身能力不足
我讓網路時間等待長了一倍,也就是性能上差了一倍
如果是使用協程
launch(Dispatchers.Main) { val userDetails = async { api.getUserDetails(user) // 獲取用戶詳細資料 } val userPosts = async { api.getUserPosts(user) // 獲取用戶發布的帖子 } val combinedData = suspendingMerge(userDetails, userPosts) // 合併資料 // 接下來可以處理合併後的資料 }
coroutine由於消除了併發之間協作的難度
可以輕鬆地寫出複雜的併發代碼
甚至有些不可能實現的併發任務,變成可能甚至變得很簡單
這些才是協程的優勢所在
但這時你可能會有個疑惑
你要在後台執行任務?切協程
要在主線程執行任務?切協程
launch(Dispatchers.IO) { val fileData = readFile(fileId) // 在IO線程中讀取文件數據 launch(Dispatchers.Main) { fileTextView.setText(fileData) // 在主線程中更新文本視圖 } }
這不也是callback地獄?
讓code很髒很不簡潔?
這邊就要講到
coroutine有一個很厲害的函數 withContext
launch(Dispatchers.Main) { val fileContent = withContext(Dispatchers.IO) { readFile(fileId) // 在IO線程中讀取文件內容 } fileTextView.setText(fileContent) // 在主線程中設置文本視圖的內容 }
可以寫成這樣,看起來區別不大
但如果你有更多的線程切換,優勢就體現出來了
由於有自動切回的功能,Coroutine消除了併發代碼在協作時的嵌套
launch(Dispatchers.IO){ ... launch(Dispatchers.Main){ ... launch(Dispatchers.IO){ ... launch(Dispatchers.Main){ } } } }
由於有了『自動切回來的功能』
寫成消除了在併發代碼協作時的嵌套
直接寫成上下關係代碼
就能讓Coroutine之間進行協作
launch(Dispatchers.Main){ withContext(Dispatchers.IO){ ... } ... withContext(Dispatchers.IO){ ... } ... }
這就是協程
『協作式的例程』
而且由於消除了嵌套
你可以把withContext放到函數裡面
用她包著函數的實際業務代碼
但function要在前方用掛起函數
launch(Dispatchers.Main) { val fileContent = suspendingReadFile(fileId) fileTextView.setText(fileContent) } suspend fun suspendingReadFile(fileId: String): String { return withContext(Dispatchers.IO) { readFile(fileId) // 在IO線程中讀取文件 } }
suspend是coroutine最核心的關鍵詞
他也是最難懂,最多被誤解的點
『suspend是掛起,非阻塞式的,他並不會阻擋你的線程』
這你看得懂嗎? 我一開始是看不懂
再說一次
協程是什麼?
一個線程框架
好在哪?好在方便
最方便的地方在哪?
他可以在同一個代碼塊中做線程切換
沒有留言:
張貼留言