如何使用协同程序依次执行操作?

时间:2020-03-13 23:09:58

标签: android kotlin-coroutines

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

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!"
    }
}

2 个答案:

答案 0 :(得分:2)

您可以使用一个频道来播放其缓冲容量,如下所示:

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

答案 1 :(得分:0)

Channels是实现此目标的好方法,但是您也可以创建自己的CoroutineDispatcher,例如

val coroutineScope = CoroutineScope(Executors.newSingleThreadExecutor().asCoroutineDispatcher())
        btn.setOnClickListener {
            coroutineScope.launch {
                channel.send(Unit)
            }
        }