Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,被称之为 Android 世界的Swift。Kotlin 可以编译成Java字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在Google I/O 2017中,Google 宣布 Kotlin 成为 Android 官方开发语言。Kotlin也能够完美兼容JAVA代码,可以做到在同一个项目中使用Kotlin与JAVA混合开发。
在Android项目中使用Kotlin进行开发,可以减少开发的代码量,得到一种更加极致的开发体验。
1.协程概念
官方解释: 协程通过将复杂性放入库中来简化异步编程。程序的逻辑可以在协程中顺序地表达,而底层库会为我们解决其异步性。该库可以将用户代码的相关部分包装为回调、订阅相关事件、在不同线程(甚至不同机器)上调度执行,而代码则保持如同顺序执行一样简单。
简单理解协程与线程功能相似,都是用来可以处理异步任务。但协程与线程却是不同的。线程切换和阻塞的开销比较大,并且程序不能控制调度机制,线程的执行顺序完全由系统调度完成。而协程依赖于线程,但是协程挂起时可以不阻塞线程,几乎是无代价的,协程是由开发者控制的。所以协程也像用户态的线程,非常轻量级,一个线程中可以创建任意个协程。
2.协程的简单使用
前面说了这么多的概念,其具体的使用方式和注意事项如下
开发环境
Android studio 4.1.1
2.1依赖导入
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
2.2 协程创建方式
在Kotlin中提供了协程的处理类GlobalScope,在GlobalScope中提供了以下几种方式创建协程。
- launch 创建协程,执行的耗时任务不会阻塞线程
- async 创建带返回值的协程,返回的是 Deferred 类,可同步执行多个协程代码块,执行的耗时任务不会阻塞线程
- withContext 不创建新的协程,指定协程上运行代码块,执行的耗时任务会阻塞上下文协程
2.2.1 使用launch创建协程
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//绑定按钮单击事件
launch_btn.onClick { launchCreateCoroutines() }
}
/**
* description:通过launch方式创建协程
* params:
* return:
*/
private fun launchCreateCoroutines(){
//在主线程中创建协程
GlobalScope.launch{
val result = doCoroutines()
Log.e("TAG","${result},当前线程id:${Thread.currentThread().id}")
}
Log.e("TAG","launchCreateCoroutines执行完成,当前线程id:${Thread.currentThread().id}")
}
/**
* description:协程挂起函数
* params:
* return:
*/
private suspend fun doCoroutines():String{
//暂停两秒,模拟耗时操作
delay(2000L)
Log.e("TAG","doCoroutines完成,当前线程id:${Thread.currentThread().id}")
return "成功执行doCoroutines"
}
}
结果
其中 suspend 表示在协程中使用的方法,suspend修饰的方法不可以直接使用,只能在协程中调用。在执行到suspend方法时,会将协程挂起,协程挂起后不会阻塞其他线程的执行。
2.2.2 使用async创建协程
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//绑定按钮单击事件
async.onClick { asyncCreateCoroutines() }
}
private fun asyncCreateCoroutines(){
GlobalScope.launch {
val result1 = GlobalScope.async {
getAsyncResult1()
}
val result2 = GlobalScope.async {
getAsyncResult2()
}
val result = result1.await() + result2.await()
Log.e("TAG","result = $result")
}
Log.e("TAG","asyncCreateCoroutines执行完成,当前线程id:${Thread.currentThread().id}")
}
private suspend fun getAsyncResult1(): Int {
repeat(3){
delay(1000)
Log.e("TAG","Async1:${it},当前线程id:${Thread.currentThread().id}")
}
return 1
}
private suspend fun getAsyncResult2(): Int {
repeat(5){
delay(1000)
Log.e("TAG","Async2:${it},当前线程id:${Thread.currentThread().id}")
}
return 2
}
}
结果
async 同样不会阻塞线程,可以拥有返回值,并且 async 是支持并发执行的,此时一般都跟 await 方法一起使用。执行 async 协程不会阻塞上下文协程,只有在调用 await 方法获取返回值时,才会阻塞上下文协程。
2.2.3 使用withContext创建协程
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//绑定按钮单击事件
withContext_btn.onClick { withContextCoroutines() }
}
private fun withContextCoroutines(){
GlobalScope.launch(Dispatchers.IO) {
Log.e("TAG","当前线程id:${Thread.currentThread().id}")
val t = withContext(Dispatchers.IO){
delay(2000L)
Log.e("TAG","withContext执行完成,当前线程id:${Thread.currentThread().id}")
"withContext"
}
Log.e("TAG","GlobalScope.launch执行完成,${t}")
}
}
}
结果
使用 withContext 必须通过 Dispatchers 来指定代码块所运行的线程, withContext 不会创建新的线程来执行代码,但是会阻塞上下文协程的执行,同时也可以将最后一行代码执行结果当中返回值。
2.3 协程上下文
在Kotlin协程中,我们可以通过设置协程上下文来设置协程代码执行在哪个线程上。Kotlin提供了以下调度器给我们使用
-
Dispatchers.Main:在 Android 主线程上运行一个协程,可以直接在协程中更新UI 。
-
Dispatchers.IO:被优化在主线程之外执行磁盘或网络 I/O。
-
Dispatchers.Default:可以在主线程之外执行 cpu 密集型的工作。例如对列表进行排序和解析 JSON。
-
Dispatchers.Unconfined:在调用的线程直接执行。
使用方式
//在主线程中创建协程
GlobalScope.launch(Dispatchers.Main){
//do something
}
3 总结
在日常开发中,使用协程可以更好的控制任务执行,并且比线程更加的节省资源,更加的高效。
代码git地址:https://gitee.com/fylds/kotlin_coroutines.git
本文在学习阶段编写,若有错误之处,还请指正。