Kotlin学习手记——协程初步(1)

在这里插入图片描述

在这里插入图片描述

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)

}

}

}

suspend fun SymCoroutine.yield(value: R): P {

return body.yield(value)

}

}

class Dispatcher: ContinuationInterceptor {

override val key = ContinuationInterceptor

private val executor = Executors.newSingleThreadExecutor()

//拦截器方法

override fun interceptContinuation(continuation: Continuation): Continuation {

return DispatcherContinuation(continuation, executor)

}

}

class DispatcherContinuation(val continuation: Continuation, val executor: Executor): Continuation by continuation { //接口代理

//切换线程 再resumeWith

override fun resumeWith(result: Result) {

executor.execute {

continuation.resumeWith(result)

}

}

}

suspend fun main() {

//生产者 接收Unit,返回Int类型

val producer = Coroutine.create<Unit, Int>(Dispatcher()) {

log(“producer start”)

for(i in 0…3){

log(“send”, i)

yield(i)

}

200

最后

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

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

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

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

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

continuation.resumeWith(result)

}

}

}

suspend fun main() {

//生产者 接收Unit,返回Int类型

val producer = Coroutine.create<Unit, Int>(Dispatcher()) {

log(“producer start”)

for(i in 0…3){

log(“send”, i)

yield(i)

}

200

最后

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

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

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

[外链图片转存中…(img-4cdKCvUJ-1715679847354)]

[外链图片转存中…(img-wRPuQGZx-1715679847356)]

[外链图片转存中…(img-8HXYwLt2-1715679847357)]

[外链图片转存中…(img-mSN7CRk7-1715679847358)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

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

  • 17
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin协程是一种轻量级的线程处理机制,它可以在不创建新线程的情况下实现异步操作。Kotlin协程的优势在于它提供了一种结构化并发的方式,使得异步代码更加易于编写和维护。下面是深入理解Kotlin协程的一些方法和步骤: 1. 协程的基本概念:协程是一种轻量级的线程处理机制,它可以在不创建新线程的情况下实现异步操作。协程的本质是一种协作式的多任务处理机制,它可以在同一个线程中切换执行不同的任务,从而实现异步操作。 2. 协程的使用方法:在Kotlin中,协程的使用方法非常简单。首先需要导入kotlinx.coroutines库,然后使用launch函数创建一个协程。在协程中可以使用挂起函数来实现异步操作,例如delay函数可以让协程暂停一段时间。 3. 协程的挂起函数:协程的挂起函数是一种特殊的函数,它可以让协程暂停执行,等待某个条件满足后再继续执行。在Kotlin中,常用的挂起函数包括delay函数、withContext函数和async函数等。 4. 协程的上下文:协程的上下文是一种特殊的对象,它包含了协程的执行环境和状态信息。在Kotlin中,协程的上下文可以通过CoroutineContext对象来表示,它包含了协程的调度器、异常处理器和其他一些属性。 5. 协程的异常处理:协程的异常处理是一种特殊的机制,它可以让协程在发生异常时自动恢复或者终止执行。在Kotlin中,协程的异常处理可以通过try-catch语句或者CoroutineExceptionHandler对象来实现。 6. 协程的取消:协程的取消是一种特殊的机制,它可以让协程在不需要继续执行时自动终止。在Kotlin中,协程的取消可以通过cancel函数或者协程作用域来实现。 下面是一个使用Kotlin协程实现异步操作的例子: ```kotlin import kotlinx.coroutines.* fun main() = runBlocking { val job = launch { delay(1000L) println("World!") } println("Hello,") job.join() } ``` 输出结果为: ``` Hello, World! ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值