首页 > 解决方案 > 如何使用协程连续执行操作?

问题描述

我知道在单个协程范围内,其中的所有操作都是按顺序执行的。但是为什么它不能以这种方式工作以及如何使其工作。预期的结果是我多次单击该按钮,它会一一处理我的所有点击,因此第 10 个 Hello World 必须在 10 秒内登录。但实际上我所有的点击都是异步工作的,第 10 次 Hello World 登录约 2 秒

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val coroutineScope = CoroutineScope(Dispatchers.IO)
        btn.setOnClickListener {
            coroutineScope.launch {
                Log.d("myTag",hello())
            }
        }
    }

    suspend fun hello(): String {
        delay(1000)
        return "Hello, World!"
    }
}

标签: androidkotlin-coroutines

解决方案


您可以使用 Channel 并使用其缓冲容量,如下所示:

class MainActivity : AppCompatActivity() {

    val channel = Channel<Unit>(10) //Supports up to 10 clicks unattended

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val coroutineScope = CoroutineScope(Dispatchers.IO)
        btn.setOnClickListener {
            coroutineScope.launch {
                channel.send(Unit)
            }
        }

        coroutineScope.launch {
            for(i in channel) {
                Log.d("myTag",hello())
            }
        }
    }

    suspend fun hello(): String {
        delay(1000)
        return "Hello, World!"
    }
}

或者,如果您愿意,您可以将点击作为流来使用,如下所示:

class MainActivity : AppCompatActivity() {

    val channel = Channel<Unit>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val coroutineScope = CoroutineScope(Dispatchers.IO)
        btn.setOnClickListener {
            coroutineScope.launch {
                channel.send(Unit)
            }
        }

        val clicksFlow = channel.consumeAsFlow()

        coroutineScope.launch {
            clicksFlow
                .buffer(Channel.BUFFERED) //Supports up to 64 clicks unattended
                .collect {
                    Log.d("myTag",hello())
                }
        }
    }

    suspend fun hello(): String {
        delay(1000)
        return "Hello, World!"
    }
}

只要确保适当地关闭channel和取消即可coroutineScope


更新

您可以使用callbackFlow(如@MarkoTopolnik 建议的),如下所示:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val coroutineScope = CoroutineScope(Dispatchers.IO)

        val clicksFlow = callbackFlow<Unit> {
            btn.setOnClickListener {
                offer(Unit)
            }
            awaitClose {
                cancel()
            }
        }

        coroutineScope.launch {
            clicksFlow
                .buffer(Channel.BUFFERED) //Supports up to 64 clicks unattended
                .collect {
                    Log.d("myTag",hello())
                }
        }
    }

    suspend fun hello(): String {
        delay(1000)
        return "Hello, World!"
    }
}

现在您只需要确保coroutineScope适当地取消即可。


推荐阅读