協程是什么?
進程、線程,在開發中使用最多的就是線程,比如主線程、子線程,而且操作系統里最小可操作的單元就是線程,那協程又是什么?協程是比線程更小的單位,但并不是說在操作系統里最小可操作單元就從線程變成了協程,而是協程依然運行在線程上,協程是在語言上實現的比線程更小的單位。
那么你可能有疑問,既然協程還是運行在線程上,那直接使用線程不就好了嗎?但問題是往往我們用不好線程,首先創建一個線程的成本很高,在 Android 中創建一個線程,大約要消耗 1M 的內存,而且,如果使用線程池,線程間數據的同步也是一個非常麻煩復雜的事情,所以就有了協程:
可以看作是輕量級線程,創建一個協程的成本很低
可以輕松的掛起和恢復操作
支持阻塞線程的協程和不阻塞線程的協程
可以更好的實現異步和并發
如果簡單來理解 Kotlin 協程的話,就是封裝好的線程池
Kotlin協程庫:Kotlin.coroutines
實現協程的庫是 Kotlin.coroutines,點擊查看Kotlin.coroutines在 Github 上的源碼。
Kotlin 是一門支持 多平臺的語言,所以 Kotlin.coroutines 也是支持多平臺的,包括:
Kotlin/JS
Kotlin/Native 包括 PC 和 Android
我們使用 Kotlin.coroutines 的 Android 版本。
給 Android 工程添加 Kotlin 協程庫
suspend 方法
在前面介紹協程的代碼里,有個不起眼的函數:
suspendfunfetchData():String{
delay(2000)return"content"
}
suspend方法是協程里的特有方法。
suspend 方法的定義
suspend方法的聲明很簡單,只需在方法 或 Lambda 定義前面加suspend關鍵字即可。
suspend 方法的使用限制
suspend 方法使用由限制,只能有兩個地方允許使用suspend方法:
在協程內部使用
在另一個 suspend 方法里使用
如果你在一個普通方法內存使用 suspend 方法,是會報語法錯誤的。
suspend 方法的功能
suspend 方法能夠使協程執行暫停,等執行完畢后在返回結果,同時不會阻塞線程。
是不是很神奇,只暫停協程,但不阻塞線程。
而且暫停協程里方法的執行,直到方法返回結果,這樣也不用寫 Callback 來取結果,可以使用同步的方式來寫異步代碼。
Coroutine context 與 Coroutine dispatchers
想要使用協程,還有兩個重要的元素:
Coroutine context:協程上下文
Coroutine dispatchers :協程調度
Coroutine context:協程上下文
協程上下文里是各種元素的集合。具體的之后的文章在講。
Coroutine dispatchers :協程調度
我們已經知道協程是運行在線程上的,我們獲取數據后要更新 UI ,但是在 Android 里更新 UI 只能在主線程,所以我們要在子線程里獲取數據,然后在主線程里更新 UI。這就需要改變協程的運行線程,這就是 Coroutine dispatchers 的功能了。
Coroutine dispatchers 可以指定協程運行在 Android 的哪個線程里。
我們先看下 dispatchers 有哪些種類:
var job = GlobalScope.launch(Dispatchers.Main) {
? ? ? ? var content = fetchData()
? ? ? ? Log.d("Coroutine",content)
? ? }
GlobalScope.launch 后面的Dispatchers.Main就是指定協程在 Android 主線程里運行。
那么,如何切換線程呢?如下:
GlobalScope.launch(Dispatchers.IO) {
? ? ...
? ? withContext(Dispatchers.Main) {
? ? ? ? ...
? ? }
}
使用withContext切換協程,上面的例子就是先在 IO 線程里執行,然后切換到主線程。
Android 開發中使用 協程
講完協程的基本用法,你還是不知道改如何用到自己的代碼里,這里給出一個最基本的用法,后續的使用方法會不斷補充。
首先 MainActivity 要 實現CoroutineScope這個接口,CoroutineScope的實現教由代理類MainScope,
class MainActivity : AppCompatActivity(),CoroutineScope by MainScope()
class MainActivity : AppCompatActivity(),CoroutineScope by MainScope() {
? ? override fun onCreate(savedInstanceState: Bundle?) {
? ? ? ? super.onCreate(savedInstanceState)
? ? ? ? setContentView(R.layout.activity_main)
? ? ? ? setSupportActionBar(toolbar)
? ? ? ? //加載并顯示數據
? ? ? ? loadDataAndShow()
? ? }
? ? fun loadDataAndShow(){
? ? ? ? GlobalScope.launch(Dispatchers.IO) {
? ? ? ? ? ? //IO 線程里拉取數據
? ? ? ? ? ? var result = fetchData()
? ? ? ? ? ? withContext(Dispatchers.Main){
? ? ? ? ? ? ? ? //主線程里更新 UI
? ? ? ? ? ? ? ? text.setText(result)
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? suspend fun fetchData():String{
? ? ? ? delay(2000)
? ? ? ? return "content"
? ? }
? ? override fun onDestroy() {
? ? ? ? super.onDestroy()
? ? ? ? //取消掉所有協程內容
? ? ? ? cancel()
? ? }
}