}
它有一个上下文对象,还有一个resumeWith
方法,这个方法就是用来恢复挂起函数用的,如果是成功和失败回调都可以通过result参数来返回。
添加了suspend
关键字的函数,kotlin最终生成下面的方法:
可以看到,suspend
函数实际上需要一个continuation参数
如果挂起函数没有真正被挂起(没有发生线程切换)返回值返回的就是实际参数类型,否则返回的是一个标记。
suspend fun getUserSuspend(name: String) = suspendCoroutine { continuation ->
githubApi.getUserCallback(name).enqueue(object: Callback{
override fun onFailure(call: Call, t: Throwable) =
continuation.resumeWithException(t)
override fun onResponse(call: Call, response: Response) =
response.takeIf { it.isSuccessful }?.body()?.let(continuation::resume)
?: continuation.resumeWithException(HttpException(response))
})
}
suspend fun main(){
val user = getUserSuspend(“bennyhuo”)
showUser(user)
}
最简单的复写挂起函数的回调:
suspend fun suspendFunc() = suspendCoroutine {
it.resumeWith(Result.success(1))
}
只不过真正的挂起需要真正的切换线程,如果直接调用的话相当于没有挂起。
suspend {
}.createCoroutine(object: Continuation{ //创建协程
override val context = EmptyCoroutineContext
override fun resumeWith(result: Result) {
log(“Coroutine End with $result”)
}
}).resume(Unit) //恢复
suspend {
}.startCoroutine(object: Continuation{ //启动协程
override val context = EmptyCoroutineContext
override fun resumeWith(result: Result) {
log(“Coroutine End with $result”) //协程执行完后调用
}
})
简单的例子,异步转同步,先使用retrofit的api创建一个接口请求实例:
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import retrofit2.Call
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
import retrofit2.http.Path
val githubApi by lazy {
val retrofit = retrofit2.Retrofit.Builder()
.client(OkHttpClient.Builder().addInterceptor(Interceptor {
it.proceed(it.request()).apply {
log(“request: ${code()}”)
}
}).build())
.baseUrl(“https://api.github.com”)
.addConverterFactory(GsonConverterFactory.create())
.build()
retrofit.create(GitHubApi::class.java)
}
interface GitHubApi {
@GET(“users/{login}”)
fun getUserCallback(@Path(“login”) login: String): Call
@GET(“users/{login}”)
suspend fun getUserSuspend(@Path(“login”) login: String): User
}
data class User(val id: String, val name: String, val url: String)
常见的调用场景:
import com.bennyhuo.kotlin.coroutinebasics.api.User
import com.bennyhuo.kotlin.coroutinebasics.api.githubApi
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
//普通的异步请求,成功和失败需要分开在两个回调函数中处理
fun async() {
val call = githubApi.getUserCallback(“bennyhuo”)
call.enqueue(object : Callback {
override fun onFailure(call: Call, t: Throwable) {
showError(t)
}
override fun onResponse(call: Call, response: Response) {
response.body()?.let(::showUser) ?: showError(NullPointerException())
}
})
}
//for循环中发起多个异步请求,获取请求结果
fun asyncLoop() {
val names = arrayOf(“abreslav”,“udalov”, “yole”)
names.forEach { name ->
val call = githubApi.getUserCallback(name)
call.enqueue(object : Callback {
override fun onFailure(call: Call, t: Throwable) {
showError(t)
}
override fun onResponse(call: Call, response: Response) {
response.body()?.let(::showUser) ?: showError(NullPointerException())
}
})
}
}
//使用挂起函数来请求,不需要请求的回调
suspend fun coroutine(){
val names = arrayOf(“abreslav”,“udalov”, “yole”)
names.forEach { name ->
try {
val user = githubApi.getUserSuspend(name) //请求后这里会挂起,直到请求成功之后恢复执行
showUser(user)
} catch (e: Exception) {
showError(e)
}
}
}
//通过挂起函数的方式获取所有异步请求的结果放到一个数组当中
suspend fun coroutineLoop(){
val names = arrayOf(“abreslav”,“udalov”, “yole”)
val users = names.map { name ->
githubApi.getUserSuspend(name)
}
}
实例:模仿Python的序列生成器Generator
import kotlin.coroutines.*
interface Generator {
operator fun iterator(): Iterator
}
class GeneratorImpl(private val block: suspend GeneratorScope.(T) -> Unit, private val parameter: T): Generator {
override fun iterator(): Iterator {
return GeneratorIterator(block, parameter)
}
}
//密封类的使用 定义状态
sealed class State {
//continuation作为参数方便下次调用
class NotReady(val continuation: Continuation): State()
class Ready(val continuation: Continuation, val nextValue: T): State()
object Done: State()
}
//GeneratorScope.(T) 点左边的是receiver
-
class GeneratorIterator(private val block: suspend GeneratorScope.(T) -> Unit, override val parameter: T)
- GeneratorScope(), Iterator, Continuation<Any?> {
override val context: CoroutineContext = EmptyCoroutineContext
private var state: State
init {
val coroutineBlock: suspend GeneratorScope.() -> Unit = { block(parameter) } //挂起函数 调用block lambda表达式传入parameter参数
val start = coroutineBlock.createCoroutine(this, this) //不需要马上启动的话使用createCoroutine创建协程
state = State.NotReady(start) //初始状态肯定是NotReady,createCoroutine返回的start参数就是Continuation类型
println(“init====================”)
}
//yield是一个挂起函数 覆写GeneratorScope类的方法
override suspend fun yield(value: T) = suspendCoroutine {
continuation ->
println(“yield======== s t a t e . j a v a C l a s s . s i m p l e N a m e v a l u e = {state.javaClass.simpleName} value= state.javaClass.simpleNamevalue={value}”)
state = when(state) {
is State.NotReady -> State.Ready(continuation, value) //调用yield(xx)方法使状态进入Ready状态
is State.Ready<*> -> throw IllegalStateException(“Cannot yield a value while ready.”)
State.Done -> throw IllegalStateException(“Cannot yield a value while done.”)
}
//这里continuation没有直接调用resume方法,在后面用户调用hasNext()或next()时调用resume()
}
private fun resume() {
println(“resume()====================”)
//val currentState = state之后调用.continuation会自动类型转换
when(val currentState = state) {
is State.NotReady -> {
println(“resume()====================when NotReady”)
currentState.continuation.resume(Unit) // NotReady时调用Continuation的resume方法恢复挂起点继续执行
}
}
}
override fun hasNext(): Boolean {
println(“hasNext()====================”)
resume()
return state != State.Done
}
//next方法返回yield存入的值
override fun next(): T {
println(“next()========${state.javaClass.simpleName}”)
return when(val currentState = state) {
is State.NotReady -> {
resume()
return next() //NotReady时调用下次的next
}
is State.Ready<*> -> {
state = State.NotReady(currentState.continuation) //state状态流转
println(“next()====return value”)
(currentState as State.Ready).nextValue //Ready时才取值返回
}
State.Done -> throw IndexOutOfBoundsException(“No value left.”) //Done状态调用next()抛异常
}
}
//协程体执行完毕
override fun resumeWith(result: Result<Any?>) {
println(“resumeWith====================”)
state = State.Done
result.getOrThrow()
}
}
//这个是定义一个receiver类,保证yield()方法只能在lambda表达式的大括号内使用
abstract class GeneratorScope internal constructor(){
protected abstract val parameter: T
abstract suspend fun yield(value: T)
}
//返回值具有迭代器功能
fun generator(block: suspend GeneratorScope.(T) -> Unit): (T) -> Generator {
return { parameter: T ->
println(“parameter = $parameter”) // parameter = 10 这个是generator接收的start函数,nums(10)
GeneratorImpl(block, parameter)
}
}
fun main() {
val nums = generator { start: Int ->
for (i in 0…5) {
yield(start + i) //yield会挂起函数调用处
}
}
val seq = nums(10)
//println(seq.iterator().next())
for (j in seq) {
println(j)
}
//kotlin官方提供的sequence序列中有yield方法
val sequence = sequence {
yield(1)
yield(2)
yield(3)
yield(4)
yieldAll(listOf(1,2,3,4))
}
for(xx in sequence){
println(xx)
}
}
第一次看这个例子还是比较绕比较难理解的,我添加了log输出,打印执行的顺序:
从log输出的顺序可以看出是每次for循环会先调用hasNext()
方法,hasNext()
中会调用resume()
方法,第一次调用resume()
相当于启动协程,这时协程会执行到yield调用处的代码yield(start + i)
这行并挂起(实际上是一个lambda表达式,它是一个Continuation的实现类SuspendLambad的包装),那yield方法中就会向state中存入值,并同时保存当前的Continuation对象,然后流传状态变化,变成ready, 紧接着for循环里取值操作会调用next
方法,然后在下一次的for循环中又会调用resume()
方法,这时就是恢复前面一次挂起函数调用处的代码继续执行,也就是执行下一次for循环里yield(start + i)
会继续放下一个值,又挂起,next()
方法又取值,hasNext()
方法又resume()
恢复, 继续执行yeild…循环往复。
如果不调用for循环打印,直接调用next获取值呢
fun main() {
val nums = generator { start: Int ->
for (i in 0…5) {
yield(start + i) //yield会挂起函数调用处
}
}
val seq = nums(10)
println(seq.iterator().next())
}
这时next方法中也会先resume, 相当于启动协程,协程体里包含的lambda表达式开始执行,yield方法存值并挂起,next()方法取值,但这时好像没有调用resumeWith方法。。但还是能正常执行完毕。
这个例子比较绕的一点就是协程体中的代码并不会立即被执行,也就是下面的代码:
fun main() {
val nums = generator { start: Int ->
for (i in 0…5) {
yield(start + i) //yield会挂起函数调用处
}
}
val seq = nums(10) //这样并不会执行上面的for循环里面的代码
// for (j in seq) {
// println(j)
// }
你会发现把下面调用的for循环代码注释掉,上面的lambda表达式里面的代码并不会执行,不信看log输出:
只输出了一个parameter=10,yield方法里面的log一个也没有打印。很神奇,就是说只有调用获取值的时候,才会执行yield方法给你发送值,调用一次发送一次,kotlin官方提供的sequence
序列大括号中的yield方法调用也是如此,sequence
本身可以当做迭代器使用。
实例:仿 Lua 协程实现非对称协程 API
sealed class Status {
class Created(val continuation: Continuation): Status()
class Yielded
(val continuation: Continuation
): Status()
class Resumed(val continuation: Continuation): Status()
object Dead: Status()
}
class Coroutine<P, R> (
override val context: CoroutineContext = EmptyCoroutineContext,
private val block: suspend Coroutine<P, R>.CoroutineBody.§ -> R //receiver是Coroutine<P, R>.CoroutineBody 内部类
): Continuation {
companion object {
fun <P, R> create(
context: CoroutineContext = EmptyCoroutineContext,
block: suspend Coroutine<P, R>.CoroutineBody.§ -> R
): Coroutine<P, R> {
return Coroutine(context, block)
}
}
//内部类,保证yield()方法不能在外部调用 只能在lambda当中调用
inner class CoroutineBody {
var parameter: P? = null
suspend fun yield(value: R): P = suspendCoroutine { continuation ->
val previousStatus = status.getAndUpdate {
when(it) {
is Status.Created -> throw IllegalStateException(“Never started!”)
is Status.Yielded<*> -> throw IllegalStateException(“Already yielded!”)
is Status.Resumed<*> -> Status.Yielded(continuation)
Status.Dead -> throw IllegalStateException(“Already dead!”)
}
}
(previousStatus as? Status.Resumed)?.continuation?.resume(value)
}
}
private val body = CoroutineBody()
private val status: AtomicReference
val isActive: Boolean
get() = status.get() != Status.Dead
init {
val coroutineBlock: suspend CoroutineBody.() -> R = { block(parameter!!) }
val start = coroutineBlock.createCoroutine(body, this)
status = AtomicReference(Status.Created(start))
}
override fun resumeWith(result: Result) {
val previousStatus = status.getAndUpdate {
when(it) {
is Status.Created -> throw IllegalStateException(“Never started!”)
is Status.Yielded<*> -> throw IllegalStateException(“Already yielded!”)
is Status.Resumed<*> -> {
Status.Dead
}
Status.Dead -> throw IllegalStateException(“Already dead!”)
}
}
(previousStatus as? Status.Resumed)?.continuation?.resumeWith(result)
}
suspend fun resume(value: P): R = suspendCoroutine { continuation ->
val previousStatus = status.getAndUpdate {
when(it) {
is Status.Created -> {
body.parameter = value
Status.Resumed(continuation)
}
is Status.Yielded<*> -> {
Status.Resumed(continuation)
}
is Status.Resumed<*> -> throw IllegalStateException(“Already resumed!”)
Status.Dead -> throw IllegalStateException(“Already dead!”)
}
}
when(previousStatus){
is Status.Created -> {
previousStatus.continuation.resume(Unit)
}
is Status.Yielded<*> -> {
(previousStatus as Status.Yielded
).continuation.resume(value)
}
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
光有这些思路和搞懂单个知识的应用是还远远不够的,在Android开源框架设计思想中的知识点还是比较多的,想要搞懂还得学会整理和规划:我们常见的**Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、IOC依赖注入框架、最近架构组件Jetpack等等Android第三方开源框架,**这些都是属于Android开源框架设计思想的。如下图所示:
这位阿里P8大佬针对以上知识点,熬夜整理出了一本长达1042页的完整版如何解读开源框架设计思想PDF文档,内容详细,把Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、IOC依赖注入框架、最近架构组件Jetpack等等Android第三方开源框架这些知识点从源码分析到实战应用都讲的简单明了。
由于文档内容过多,篇幅受限,只能截图展示部分
整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断!!!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
光有这些思路和搞懂单个知识的应用是还远远不够的,在Android开源框架设计思想中的知识点还是比较多的,想要搞懂还得学会整理和规划:我们常见的**Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、IOC依赖注入框架、最近架构组件Jetpack等等Android第三方开源框架,**这些都是属于Android开源框架设计思想的。如下图所示:
[外链图片转存中…(img-ZuCUqb1n-1712662343113)]
这位阿里P8大佬针对以上知识点,熬夜整理出了一本长达1042页的完整版如何解读开源框架设计思想PDF文档,内容详细,把Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、IOC依赖注入框架、最近架构组件Jetpack等等Android第三方开源框架这些知识点从源码分析到实战应用都讲的简单明了。
由于文档内容过多,篇幅受限,只能截图展示部分
[外链图片转存中…(img-RYfPOCoM-1712662343114)]
[外链图片转存中…(img-YH7wbB5Z-1712662343114)]
整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断!!!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!