浅谈Kotlin协程使用

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
本文在学习阶段编写,若有错误之处,还请指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值