抬头仰望星空,是否能发现自己的渺小。

伪斜杠青年

人们总是混淆了欲望和理想

扔老师的Kotlin协程手札

只写只言片语

拒绝胡搅蛮缠

协程本身的定义

协程本身的定义:见wiki(协程

扔老师的解释是:协程是一种在程序中处理并发任务的方案,也是这种方案的一个组件,与线程属于同一个层级的概念。协程中不存在线程,也不存在并行。

我对此表示认同,因为之前就被公司大哥批评过,纠正了我以前对协程的看法。这是一种协调执行程序的方案,是CPU核心进行的并发操作,与线程无关。

本人的简要理解:“有那么点理解 但无法具体化解释”。

Kotlin协程的定义

一个线程框架,没错就是线程框架,本质上与上面的协程没什么关系,可以看做RxJava或者线程池。

为什么用它?

语法糖写起来简单,可以拒绝套娃,方便管理,有利于处理Android生命周期改变导致的内存泄露,以及或许被Google优化后的稍微快一点点。

怎么写?

使用launch()开启协程,指定所在的线程,加上withContext()切换线程:

GlobalScope.launch {
  println("Coroutines ${Thread.currentThread().name}")
}
GlobalScope.launch(Dispatchers.Main) {
  println("Coroutines ${Thread.currentThread().name}")
}

线程的切换处理(带上返回值也是可以的):

GlobalScope.launch(Dispatchers.Main) {
 ioCode()//会在子线程处理
 println("Coroutines ${Thread.currentThread().name}")//会在主线程处理
}
suspend fun ioCode() {
  withContext(Dispatchers.IO) {
    //do something
    println("Coroutines io ${Thread.currentThread().name}")
  }
}

coroutineScope 小知识

伪代码:

mainScope.launch{
 coroutineScope{
   launch{
   // do a
   }
   launch{
   // do b
   }
 }
  // do c 
}
coroutineScope这个挂起函数会使得 在 a b 两个协程执行完后再继续执行 c

如何避免内存泄露?

主要靠使用CoroutineScope的一些实现类。

MainScope统一管理:

val mainScope = MainScope()
//....//
fun someMethod() {
 mainScope.launch {

 }
}
fun someOtherMethod() {
  mainScope.launch {

 }
}
//....//
override fun onDestroy() {
 mainScope.cancel()
 super.onDestroy()
}

直接绑定生命周期,使用lifecycleScope

lifecycleScope.launch(Dispatchers.Main) {
delay(1000)
Log.d("Coroutines", "1000 毫秒后!")
}

数据的聚合处理(组合挂起函数):

GlobalScope.launch(Dispatchers.Main) {
val one = async { /*network api1*/ return@async "code1" }
val two = async { /*network api1*/ return@async "code2" }
val same = one.await() == two.await()
Log.d("Coroutines", "$same same")
}

使用async配合await可在两处请求均返回后再进行结果处理。

Koltin是怎么切线程的?

使用suspend关键字对编译时做了处理,对每个块进行了标记,不同线程执行不同代码块,直接在代码里看不到,反编译后可以看一点点。

协程为什么可以挂起却不卡主线程?

把协程改一改,这样会卡主线程么?

lifecycleScope.launch(Dispatchers.Main) {
    delay(1000)
    Log.d("Coroutines", "1000 毫秒后!")
}

thread {
    Thread.sleep(1000)
    runOnUiThread {
      Log.d("Coroutines", "1000 毫秒后!")
    }
}

彩蛋?

方法:

private fun classicIoCode(toUiThread: Boolean = true, block: () -> Unit) {}
原始调用:
(::classicIoCode).invoke(true, {})
简化:
(::classicIoCode)(true, {})
简化:
classicIoCode(true, {})
简化:
classicIoCode(true) {}
再简化(使用默认参数):
classicIoCode{}

结语

就这些?嗯,就这些,1块钱的课还想要多少。。。好像还讲了些源码以及配合retrofit,rxJava的,但是忘了,因为要用可以看官方文档啊。


0条评论

发表评论