最近在研究 Kotlin 協程,發現功能真的超級強大,頗有用,並且很好學,若是你正在或計劃使用 Kotlin 開發 Android,那麼 Kotlin 協程你必定不能錯過!android
咱們日常接觸的都是進程、線程,在開發中使用最多的就是線程,好比主線程、子線程,並且操做系統裏最小可操做的單元就是線程,那協程又是什麼?協程是比線程更小的單位,但並非說在操做系統裏最小可操做單元就從線程變成了協程,而是協程依然運行在線程上,協程是在語言上實現的比線程更小的單位。git
那麼你可能有疑問,既然協程仍是運行在線程上,那直接使用線程不就行了嗎?但問題是每每咱們用很差線程,首先建立一個線程的成本很高,在 Android 中建立一個線程,大約要消耗 1M 的內存,並且,若是使用線程池,線程間數據的同步也是一個很是麻煩複雜的事情,因此就有了協程:github
若是簡單來理解 Kotlin 協程的話,就是封裝好的線程池。bash
實現協程的庫是 Kotlin.coroutines,點擊查看 Kotlin.coroutines 在 Github 上的源碼。併發
Kotlin 是一門支持 多平臺的語言,因此 Kotlin.coroutines 也是支持多平臺的,包括:app
咱們使用 Kotlin.coroutines 的 Android 版本。異步
要使用協程,Kotlin 的版本必須在1.3以上。 ide
在 Android Studio 中選擇 Android Stuido
-> Preference...
-> Languages & Framewroks
-> Kotlin
函數
在這裏升級 Kotlinfetch
在 Android Studio 中選擇 File
-> New
-> New Project...
在這個界面裏選中 Include Kotlin support
,剩下的和建立通常 Android 工程是同樣的。
在 app/build.gradle
裏添加 Kotlin 協程庫的依賴:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'
複製代碼
建立Coroutine須要使用 Coroutine Builder 函數,包括:
做用 | |
---|---|
launch | 建立一個不會阻塞當前線程、沒有返回結果的 Coroutine,但會返回一個 Job 對象,能夠用於控制這個 Coroutine 的執行和取消 |
runBlocking | 建立一個會阻塞當前線程的Coroutine |
其實不止這兩個,但本篇先介紹這兩個。
使用 GlobalScope.launch
來建立協程,使用方法以下:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var job = GlobalScope.launch(Dispatchers.Main) {
var content = fetchData()
Log.d("Coroutine",content)
}
}
suspend fun fetchData():String{
delay(2000)
return "content"
}
複製代碼
Activity 的 onCreate() 裏,用 GlobalScope.launch
建立一個協程,在協程裏我模擬了一個請求,去獲取數據,而後把數據打印出來。
由於 GlobalScope.launch
是無阻塞
的,因此不會阻塞 UI 線程。
這裏 GlobalScope.launch
建立以後,會返回一個 Job 對象,Job 對象能夠這麼使用:
使用 runBlocking
來建立協程,使用方法以下:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
runBlocking {
var content = fetchData()
Log.d("Coroutine",content)
}
}
suspend fun fetchData():String{
delay(2000)
return "content"
}
複製代碼
功能和上一個例子同樣,可是這裏協程建立改爲了 runBlocking()
,可是 runBlocking()
是會阻塞線程的,因此這裏會阻塞 UI 線程,這裏是一個錯誤用例的示範(Orz...)
在前面介紹協程的代碼裏,有個不起眼的函數:
suspend fun fetchData():String{
delay(2000)
return "content"
}
複製代碼
suspend
方法是協程裏的特有方法。
suspend
方法的聲明很簡單,只需在方法 或 Lambda 定義前面加 suspend
關鍵字便可。
suspend 方法使用由限制,只能有兩個地方容許使用 suspend
方法:
若是你在一個普通方法內存使用 suspend 方法,是會報語法錯誤的。
suspend 方法可以使協程執行暫停,等執行完畢後在返回結果,同時不會阻塞線程。
是否是很神奇,只暫停協程,但不阻塞線程。
並且暫停協程裏方法的執行,直到方法返回結果,這樣也不用寫 Callback 來取結果,可使用同步的方式來寫異步代碼,真是漂亮啊。
想要使用協程,還有兩個重要的元素:
協程上下文裏是各類元素的集合。具體的以後的文章在講。
咱們已經知道協程是運行在線程上的,咱們獲取數據後要更新 UI ,可是在 Android 裏更新 UI 只能在主線程,因此咱們要在子線程裏獲取數據,而後在主線程裏更新 UI。這就須要改變協程的運行線程,這就是 Coroutine dispatchers 的功能了。
Coroutine dispatchers 能夠指定協程運行在 Android 的哪一個線程裏。
咱們先看下 dispatchers 有哪些種類:
做用 | |
---|---|
Dispatchers.Default | 共享後臺線程池裏的線程 |
Dispatchers.Main | Android主線程 |
Dispatchers.IO | 共享後臺線程池裏的線程 |
Dispatchers.Unconfined | 不限制,使用父Coroutine的現場 |
newSingleThreadContext | 使用新的線程 |
在看前面的代碼裏,細心的你確定發現:
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 線程裏執行,而後切換到主線程。
講完協程的基本用法,你仍是不知道改如何用到本身的代碼裏,這裏給出一個最基本的用法,後續的使用方法會不斷補充。
首先 MainActivity 要 實現 CoroutineScope
這個接口,CoroutineScope
的實現教由代理類 MainScope
,因此是這樣子的:
class MainActivity : AppCompatActivity(),CoroutineScope by MainScope()
複製代碼
這樣 MainActivity 就是一個協程,那麼要獲取數據,並展現在 UI 上,就能夠這麼寫:
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()
}
}
複製代碼
Kotlin 協程,你有沒有心動?
未完待續...