How to make Kotlin's LifecycleScope has sequential behaviour as Java's Executors.newSingleThreadExecutor?

huangapple go评论101阅读模式
英文:

How to make Kotlin's LifecycleScope has sequential behaviour as Java's Executors.newSingleThreadExecutor?

问题

In Java Android, 为了在不阻塞主线程的情况下实现顺序行为,我正在使用以下代码。

Java Android

  1. private static final ExecutorService executor = Executors.newSingleThreadExecutor();
  2. executor.execute(() -> task0());
  3. executor.execute(() -> task1());
  4. executor.execute(() -> task2());

上述代码将始终按照task0task1task2函数的确切顺序执行,不管函数内部发生了什么。

我对Kotlin的LifecycleScope提供的生命周期感知功能印象深刻。我尝试以以下Kotlin的LifecycleScope形式编写代码。

Kotlin Android

  1. val dispatcherForCalendar = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
  2. lifecycleScope.launch(dispatcherForCalendar) {
  3. task0()
  4. }
  5. lifecycleScope.launch(dispatcherForCalendar) {
  6. task1()
  7. }
  8. lifecycleScope.launch(dispatcherForCalendar) {
  9. task2()
  10. }

上述代码将按照task0task1task2函数的确切顺序执行,除非在函数中执行了延迟操作。

  1. 实际上,我不会在任务函数中显式插入delay代码。在这种情况下,Android系统是否仍然可以隐式执行delay操作?
  2. 如何实现与我的Java代码Executors.newSingleThreadExecutor相同的顺序行为?

谢谢。

英文:

In Java Android, to achieve sequential behavior without blocking main thread, this is the code I am using.

Java Android

  1. private static final ExecutorService executor = Executors.newSingleThreadExecutor();
  2. executor.execute(() -> task0());
  3. executor.execute(() -> task1());
  4. executor.execute(() -> task2());

The above code, will always execute in the exact order of task0, task1 and task2 functions regardless what is happening inside the functions.

I am impressed by the life cycle aware feature, offered by Kotlin's LifecycleScope. I try to write the code in the following Kotlin's LifecycleScope form.

Kotlin Android

  1. val dispatcherForCalendar = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
  2. lifecycleScope.launch(dispatcherForCalendar) {
  3. task0()
  4. }
  5. lifecycleScope.launch(dispatcherForCalendar) {
  6. task1()
  7. }
  8. lifecycleScope.launch(dispatcherForCalendar) {
  9. task2()
  10. }

The above code will executed in the exact order of task0, task1 and task2 functions, except when delay is performed in the functions.

  1. In reality, I will not insert delay code explicitly in task functions. In such a case, can Android system still perform delay operation implicitly?
  2. How I can achieve sequential behavior, same as my Java code Executors.newSingleThreadExecutor?

Thank you.

答案1

得分: 1

Regarding 1):

协程可以在任何suspend函数调用时将线程返回给调度程序,而不仅仅是在delay()调用时。

Regarding 2):

我的答案在这里展示了如何使用通道(Channel)构建一个顺序任务队列。您可以在构造函数中注入一个CoroutineScope,以便在Activity或Fragment中使用时可以传递lifecycleScope。类似这样:

  1. class JobQueue(
  2. private val scope: CoroutineScope,
  3. private val defaultContext: CoroutineContext = Dispatchers.Main
  4. ) {
  5. private val queue = Channel<Job>(Channel.UNLIMITED)
  6. init {
  7. scope.launch(Dispatchers.Default) {
  8. for (job in queue) job.join()
  9. }
  10. }
  11. fun submit(
  12. context: CoroutineContext = defaultContext,
  13. block: suspend CoroutineScope.() -> Unit
  14. ) {
  15. synchronized {
  16. val job = scope.launch(context, CoroutineStart.LAZY, block)
  17. queue.trySend(job)
  18. }
  19. }
  20. }

如果这些是阻塞的任务,您可以例如将Dispatchers.IO作为运行任务的defaultContext

英文:

Regarding 1):

The coroutines may yield the thread back to the dispatcher at any suspend function call, not just at delay() calls.

Regarding 2):

My answer here shows how you can build a sequential task queue using a Channel. You could modify the class to allow a CoroutineScope to be injected in the constructor, so lifecycleScope could be passed in when using it in an Activity or Fragment. Something like this:

  1. class JobQueue(
  2. private val scope: CoroutineScope,
  3. private val defaultContext: CoroutineContext = Dispatchers.Main
  4. ) {
  5. private val queue = Channel&lt;Job&gt;(Channel.UNLIMITED)
  6. init {
  7. scope.launch(Dispatchers.Default) {
  8. for (job in queue) job.join()
  9. }
  10. }
  11. fun submit(
  12. context: CoroutineContext = defaultContext,
  13. block: suspend CoroutineScope.() -&gt; Unit
  14. ) {
  15. synchronized {
  16. val job = scope.launch(context, CoroutineStart.LAZY, block)
  17. queue.trySend(job)
  18. }
  19. }
  20. }

If these were blocking jobs, you might for example pass Dispatchers.IO as the defaultContext for running the jobs.

huangapple
  • 本文由 发表于 2023年6月22日 00:50:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76525521.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定