menu

inline class,将原生类型封装为对象有利于提升类型安全。有很多优化细节值得留意:Zero-cost* abstractions in Kotlin - Android Developers - Medium

Android App 架构层次

collection vs sequence

eagerly collecton New collection is created for each operation. executed on the entire collection

clazily sequence

sequence intermediate .. -> terminal

elements are evaluated one by one

select

coroutine 返回 selectcause 的调用在 selectbuilder 结构体内才能调用,结构体外调用提示找不到引用。

比如

async{}.onAwait{} //只能在 select scope 内调用

因为,SelectBuilder 扩展了 SelectCause 的 invoke 方法

/**
 * Registers a clause in this [select] expression without additional parameters that does not select any value.
 */
public operator fun SelectClause0.invoke(block: suspend () -> R)

非常讨巧的做法。

第一眼看到这种 DSL 真的不知道和 selectcause 怎么和 selectbuilder 联系起来。原来是扩展了 invoke 操作法方法

select {
   selectcause{}
   ...
   selectcause{}
   selectcause{}
}

具体的实现

override fun SelectClause0.invoke(block: suspend () -> R) {
        registerSelectClause0(this@SelectBuilderImpl, block)
    }

Flow vs Channel

Flow

  • sequence
  • 冷, collect 的时候才创建一个流
  • 非并发

// 结果是 2000ms measureTimeMillis{ flow { for(i in 1..10){ delay(100) } }.collect { delay(100) } }

interface FlowCollector { suspend fun emit(value: T) }

fun <T, R> Flow.map(transform: suspend (value: T) -> R) = flow { collect { emit(transform(it)) } }

  • 执行上下文

Every flow implementation has to preserve the collector’s context.

channel

  • unbuffer send 函数中断直到接收(Unbuffered channels transfer elements when sender and receiver meet each other (aka rendezvous))
  • conflate
  • buffer

Context vs Scope

Context progagation

Context 协程运行的上下文,包含有 Job Dispatcher 等。协程本身就是 Job

Scope,持有一个单一 Context 对象的接口

fun CoroutineScope.launchSay(w: String) = launch { say(w)}

context 的数据结构很有意思(sicp 里看过的序对:(cons 1 (cons 2 (cons 3 (cons 4 nil))))),用序对来表示一张表。EmptyCouroutine 是加法零元,Element 是单一元素,元素的复合通过 CombinedContext 实现(相当于 cons),Combinedcontext 通过一个链表存储数据,左边是复合对象,右边一定是 Element。

context 每个 key 都对应一个唯一实例,所以加法运行,不满足交换律,是用加数的元素替换被加数的元素。同时保证加法运算后 interceptor 一定在 context 的表尾。

按照惯例,所有协程生成器都遵循该约定,该作用域的coroutineContext属性与在此块内运行的协程的上下文相同

Dispatcher is ContinuationInterceptor

withContext

结构化并发

couroutineScope withContext

suspend 中断点

thread and coroutin e

success or failure of parent job can be known onl when all children complete

Exception

默认异常流程

  • Child 向 Parent(Job) 抛出异常
  • Parent 取消所有 Child
  • Parent 取消本身
  • 异常向上传导(propagation)

SupervisorJob

  • Child 向 Parent 抛出异常
  • None

traps&pitfall 下面的代码,child 1 抛出异常,child 也会被取消

    scope.launch(SupervisorJob()) {
        // new coroutine -> can suspend
       launch {
            // Child 1
        }
        launch {
            // Child 2
        }
    }

为什么?因为 SupervisorJob() 是 scope.launch 的 Job 的 parent,每次 launch 都会创建一个新 Job,所以 Child 1 、2 的父母是普通的 Job 不是 SupervisorJob

协程的异常抛出指,抛到 CoroutineExceptionHandler,未处理则抛到线程的 ExceptionHandler

  • launch,异常一旦发生就会抛出
  • async,在调用 await 时抛出.但异常仍然在前发生时向上抛出了
keyboard_arrow_up