Kotlin协程:Flow基础原理_kotlin flow,HarmonyOS鸿蒙 并发 面试

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img

img
img
htt

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

...
// 保存上下文中元素数量,用于检查上下文是否变化
@JvmField
internal actual val collectContextSize = collectContext.fold(0) { count, _ -> count + 1 }
// 保存上一次的上下文
private var lastEmissionContext: CoroutineContext? = null
// 执行结束后的续体
private var completion: Continuation<Unit>? = null

// 协程上下文
override val context: CoroutineContext
    get() = completion?.context ?: EmptyCoroutineContext

// 挂起的核心方法
override fun invokeSuspend(result: Result<Any?>): Any? {
    result.onFailure { lastEmissionContext = DownstreamExceptionElement(it) }
    completion?.resumeWith(result as Result<Unit>)
    return COROUTINE_SUSPENDED
}

// 释放拦截的续体
public actual override fun releaseIntercepted() {
    super.releaseIntercepted()
}

// 发射数据
override suspend fun emit(value: T) {
    // 获取当前suspend方法续体
    return suspendCoroutineUninterceptedOrReturn sc@{ uCont ->
        try {
            // 调用重载的方法
            emit(uCont, value)
        } catch (e: Throwable) {
             // 出现异常时,将异常封装成上下文,保存到lastEmissionContext
            lastEmissionContext = DownstreamExceptionElement(e)
            // 抛出异常
            throw e
        }
    }
}

// 重载的emit方法
private fun emit(uCont: Continuation<Unit>, value: T): Any? {
    // 从续体中获取上下文
    val currentContext = uCont.context
    // 保证当前协程的Job是active的
    currentContext.ensureActive()
    // 获取上次的上下文
    val previousContext = lastEmissionContext
    // 如果前后上下文发生变化
    if (previousContext !== currentContext) {
        // 检查上下文是否发生异常
        checkContext(currentContext, previousContext, value)
    }
    // 保存续体
    completion = uCont
    // 调用emitFun方法,传入collector,value,continuation
    return emitFun(collector as FlowCollector<Any?>, value, this as Continuation<Unit>)
}

// 检查上下文变化,防止并发
private fun checkContext(
    currentContext: CoroutineContext,
    previousContext: CoroutineContext?,
    value: T
) {
    // 如果上次执行过程中发生了异常
    if (previousContext is DownstreamExceptionElement) {
        // 抛出异常
        exceptionTransparencyViolated(previousContext, value)
    }
    // 检查上下文是否发生变化,如果变化,则抛出异常
    checkContext(currentContext)
    lastEmissionContext = currentContext
}

// 用于抛出异常
private fun exceptionTransparencyViolated(exception: DownstreamExceptionElement, value: Any?) {
    error("""
        Flow exception transparency is violated:
            Previous 'emit' call has thrown exception ${exception.e}, but then emission attempt of value '$value' has been detected.
            Emissions from 'catch' blocks are prohibited in order to avoid unspecified behaviour, 'Flow.catch' operator can be used instead.
            For a more detailed explanation, please refer to Flow documentation.
        """.trimIndent())
}

}


emit方法最终会调用emitFun方法方法,代码如下:



private val emitFun =
FlowCollector<Any?>::emit as Function3<FlowCollector<Any?>, Any?, Continuation, Any?>


emitFun是一个lambda表达式,它将只有一个参数的emit方法转换成三个参数的方法。emitFun方法在编译时会被编译器处理,反编译后的代码逻辑大致如下:



@Nullable
public final Object invoke(@NotNull FlowCollector p1, @Nullable Object p2, @NotNull Continuation continuation) {
InlineMarker.mark(0);
// 核心执行
Object var10000 = p1.emit(p2, continuation);
InlineMarker.mark(2);
InlineMarker.mark(1);
return var10000;
}


可以看到,emitFun方法内部会调用FlowCollector类对象的emit方法,同时传入value和continuation作为参数。


而这个FlowCollector类对象就是一开始的collect方法封装的匿名类对象,代码如下:



public suspend inline fun Flow.collect(crossinline action: suspend (value: T) -> Unit): Unit =
collect(object : FlowCollector {
override suspend fun emit(value: T) = action(value)
})


调用它的emit方法,会直接调用action的invoke方法,并传入发射的数据,流在这里被最终消费。


通过上面的分析,可以知道消费的过程是在emit方法中被调用的,如果在消费的过程,没有发生挂起,那么emit方法执行完毕后,会继续执行flow方法里剩下的代码,而如果在消费的过程中发生了挂起,情况会稍有不同。


### 4.消费过程中的挂起


如果消费过程中发生挂起,那么emit方法会返回一个`COROUTINE_SUSPENDED`对象,`suspendCoroutineUninterceptedOrReturn`方法在收到COROUTINE\_SUSPENDED对象后,会挂起当前协程。代码如下:



override suspend fun emit(value: T) {
// 获取当前suspend方法续体
return suspendCoroutineUninterceptedOrReturn sc@{ uCont ->
try {
// 调用重载的方法
emit(uCont, value)
} catch (e: Throwable) {
// 出现异常时,将异常封装成上下文,保存到lastEmissionContext
lastEmissionContext = DownstreamExceptionElement(e)
// 抛出异常
throw e
}
}
}


当消费过程执行完毕时,会通过传入的续体唤起外部协程恢复挂起状态。根据`emitFun`可以知道,这里传入的续体为this,也就是当前的`SafeCollector`类对象,代码如下:



emitFun(collector as FlowCollector<Any?>, value, this as Continuation)


恢复挂起需要调用续体的resumeWith方法,上面提到SafeCollector类继承自ContinuationImpl类,SafeCollector类中没有重写resumeWith方法,而ContinuationImpl类中也没有重写resumeWith方法,因此实际调用的是ContinuationImpl类的父类BaseContinuationImpl类的resumeWith方法。如下图所示:


![](https://img-blog.csdnimg.cn/img_convert/bbf07ef658c8a36d8f38bec47cee6001.png)


在[Kotlin协程:创建、启动、挂起、恢复]( )中提到过,调用BaseContinuationImpl类的resumeWith方法,内部会调用invokeSuspend方法,而SafeCollector类重写了invokeSuspend方法,代码如下:



override fun invokeSuspend(result: Result<Any?>): Any? {
// 尝试获取异常
result.onFailure { lastEmissionContext = DownstreamExceptionElement(it) }
// 如果没有异常,则恢复flow方法续体的执行
completion?.resumeWith(result as Result)
// 返回挂起标识,这里挂起的是消费过程
return COROUTINE_SUSPENDED
}


在invokeSuspend方法中,会调用resumeWith方法恢复生产过程——flow方法的执行,同时挂起消费过程的执行。全部过程如下图所示:


![](https://img-blog.csdnimg.cn/img_convert/29975ee75986e5abcb2224f354277a9f.png)



> 
> 作者:李萧蝶  
>  链接:https://juejin.cn/post/7137647612286468132
> 
> 
> 


## 三、Kotlin学习资料



> 
> 一. Kotlin入门教程指南
> 
> 
> 二. 高级Kotlin强化实战
> 
> 
> 三. Android版Kotlin协程入门进阶实战
> 
> 
> 


### 《Kotlin入门教程指南》


#### **第一章 Kotlin 入门教程指南**


* 前言


​ ![img](https://img-blog.csdnimg.cn/img_convert/3ff51b383a6231cca8f98a4a815c74a9.png)


#### **第二章 概述**


* 使用 Kotlin 进行服务器端开发
* 使用 Kotlin 进行 Android 开发
* Kotlin JavaScript 概述
* Kotlin/Native 用于原生开发
* 用于异步编程等场景的协程
* Kotlin 1.1 的新特性
* Kotlin 1.2 的新特性
* Kotlin 1.3 的新特性


​ ![img](https://img-blog.csdnimg.cn/img_convert/8967c17a958efcbf2f0752536d160d5d.png)


#### **第三章 开始**


* 基本语法
* 习惯用法
* 编码规范


​ ![img](https://img-blog.csdnimg.cn/img_convert/6f2ff3772284960a9a883aa0cdc41d01.png)


#### **第四章 基础**


* 基本类型
* 包
* 控制流:if、when、for、while
* 返回和跳转


​ ![img](https://img-blog.csdnimg.cn/img_convert/839ed8e0daf14a934567b7bcdda606af.png)


#### **第五章 类与对象**


* 类与继承
* 属性与字段
* 接口
* 可见性修饰符
* 扩展
* 数据类
* 密封类
* 泛型
* 嵌套类与内部类
* 枚举类
* 对象表达式与对象声明
* Inline classes
* 委托


​ ![img](https://img-blog.csdnimg.cn/img_convert/fa8d628bd6965061134b7af91656cca9.png)


#### **第六章 函数与 Lambda 表达式**


* 函数
* 高阶函数与 lambda 表达式
* 内联函数


​ ![img](https://img-blog.csdnimg.cn/img_convert/c419719a0dd7e0c8f919366d4de28fe9.png)


#### **第七章 其他**


* 解构声明
* 集合:List、Set、Map
* 区间
* 类型的检查与转换“is”与“as”
* This 表达式
* 相等性
* 操作符重载
* 空安全
* 异常
* 注解
* 反射
* 类型安全的构建器
* 类型别名
* 多平台程序设计
* 关键字与操作符


​ ![img](https://img-blog.csdnimg.cn/img_convert/89a2fa4143b1ee52e847d2f0bb1e55fa.png)


#### **第八章 Java 互操作与 JavaScript**


* 在 Kotlin 中调用 Java 代码
* Java 中调用 Kotlin
* JavaScript 动态类型
* Kotlin 中调用 JavaScript
* JavaScript 中调用 Kotlin
* JavaScript 模块
* JavaScript 反射
* JavaScript DCE


​ ![img](https://img-blog.csdnimg.cn/img_convert/7158e0a6465dfa12306f5a12441eaec5.png)


#### **第九章 协程**


* 协程基础
* 取消与超时
* 通道 (实验性的)
* 组合挂起函数
* 协程上下文与调度器
* 异常处理
* select 表达式(实验性的)
* 共享的可变状态与并发


​ ![img](https://img-blog.csdnimg.cn/img_convert/721e02ec1c3d5daeade692e6f0e12e3b.png)


#### **第十章 工具**


* 编写 Kotlin 代码文档
* Kotlin 注解处理
* 使用 Gradle
* 使用 Maven
* 使用 Ant
* Kotlin 与 OSGi
* 编译器插件
* 不同组件的稳定性


​ ![img](https://img-blog.csdnimg.cn/img_convert/6bef50af94d70e5bf745b618decae131.png)


#### **第十一章 常见问题总结**


* FAQ
* 与 Java 语言比较
* 与 Scala 比较【官方已删除】


​ ![img](https://img-blog.csdnimg.cn/img_convert/b435c5f51d5d414d97117d1a01d49519.png)


​


### **《**高级Kotlin强化实战**》**


#### **第一章 Kotlin 入门教程**


* Kotlin 概述
* Kotlin 与 Java 比较
* 巧用 Android Studio
* 认识 Kotlin 基本类型
* 走进 Kotlin 的数组
* 走进 Kotlin 的集合
* 完整代码
* 基础语法


​ ![img](https://img-blog.csdnimg.cn/img_convert/dada14495ae74b64948e8eed682308fd.png)


#### **第二章 Kotlin 实战避坑指南**


* 方法入参是常量,不可修改
* 不要 Companion、INSTANCE?
* Java 重载,在 Kotlin 中怎么巧妙过渡一下?
* Kotlin 中的判空姿势
* Kotlin 复写 Java 父类中的方法
* Kotlin “狠”起来,连TODO都不放过!
* is、as` 中的坑
* Kotlin 中的 Property 的理解
* also 关键字
* takeIf 关键字
* 单例模式的写法


​ ![img](https://img-blog.csdnimg.cn/img_convert/97894ffb8166dc33a159bcb9ea54dd17.png)


#### **第三章 项目实战《Kotlin Jetpack 实战》**


* 从一个膜拜大神的 Demo 开始
* Kotlin 写 Gradle 脚本是一种什么体验?
* Kotlin 编程的三重境界
* Kotlin 高阶函数
* Kotlin 泛型
* Kotlin 扩展
* Kotlin 委托
* 协程“不为人知”的调试技巧
INSTANCE?
* Java 重载,在 Kotlin 中怎么巧妙过渡一下?
* Kotlin 中的判空姿势
* Kotlin 复写 Java 父类中的方法
* Kotlin “狠”起来,连TODO都不放过!
* is、as` 中的坑
* Kotlin 中的 Property 的理解
* also 关键字
* takeIf 关键字
* 单例模式的写法


​ ![img](https://img-blog.csdnimg.cn/img_convert/97894ffb8166dc33a159bcb9ea54dd17.png)


#### **第三章 项目实战《Kotlin Jetpack 实战》**


* 从一个膜拜大神的 Demo 开始
* Kotlin 写 Gradle 脚本是一种什么体验?
* Kotlin 编程的三重境界
* Kotlin 高阶函数
* Kotlin 泛型
* Kotlin 扩展
* Kotlin 委托
* 协程“不为人知”的调试技巧
  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值