【问题标题】:How launch and execute just one coroutine between ViewModel and LiveData如何在 ViewModel 和 LiveData 之间启动和执行一个协程
【发布时间】:2020-12-03 09:02:31
【问题描述】:

这是我的伪代码。

class ViewModel {
    val liveData1: MutableLiveData<String>
        private set

    val liveData2: MutableLiveData<String>
        private set

    fun start() {
        liveData1.value = "render1"
        liveData2.value = "render2"

        /**
        * I want that a Logcat shows a log like below:
        *
        * render1 start
        * render1 end
        * render2 start
        * render2 end
        *
        * but, result was
        *
        * render1 start
        * render2 start
        * render1 end
        * render2 end
        */
    }
}

class Activity { 

    fun onCreate() {
        subscribe()
        viewModel.start()
    }

    fun subscribe() {
        viewModel.livedata1.observe(this, Observer {
            lifecycleScope.launch {
                render1() 
            }
        })

        viewModel.livedata2.observe(this, Observer {
            lifecycleScope.launch {
                render2() 
            }
        })
    }

    suspend fun render1() {
        Log.d("tag", "render1 start")
        delay(1000)
        Log.d("tag", "render1 end")
    }

    suspend fun render2() {
        Log.d("tag", "render2 start")
        delay(1000)
        Log.d("tag", "render2 end")
    }

}

每次调用“launch”时,似乎启动了一个新的协程。

我想等待更新“livedata2”,直到“livedata1”渲染结束

“livedata1”渲染完成后有什么方法可以更新“livedata2”吗?

(我也必须使用 ViewModel 和 Livedata)

【问题讨论】:

    标签: android android-livedata kotlin-coroutines android-viewmodel


    【解决方案1】:
        suspend fun render1() {
            Log.d("tag", "render1 start")
            delay(1000)
            liveData2.postValue("render2")
            Log.d("tag", "render1 end")
        }
    

    【讨论】:

    • 根据 MVVM 架构,只有 ViewModel 可以 setValue 到 livedata..
    【解决方案2】:

    您似乎不需要使用lifecycleScope.launch,一切都会按您的预期正常工作。

      viewModel.livedata1.observe(this, Observer {
                render1()  
            })
    

    你也可以在主线程上启动协程:

      lifecycleScope.launch(Dispatchers.Main) {
                render1() 
           }
    

    【讨论】:

    • 如果我删除lifecycleScope.launch,编译器会抱怨“只能从协程或其他挂起函数调用挂起函数”
    • 不幸的是,没有运气
    【解决方案3】:

    由于您是在观察者内部启动协程,因此它们将同时运行,尽管它们被限制在单个线程(主线程)中。当delay 被调用时,主线程被挂起而不是被阻塞,这就是它在那段时间运行其他任务的原因。

    为避免同时运行两个渲染函数,您将不得不处理并发,例如使用互斥:

    class Activity {
    
        val myMutex = Mutex()
    
        fun onCreate() {
            subscribe()
            viewModel.start()
        }
    
        fun subscribe() {
            viewModel.livedata1.observe(this, Observer {
                lifecycleScope.launch {
                    myMutex.withLock {
                        render1()
                    }
                }
            })
    
            viewModel.livedata2.observe(this, Observer {
                lifecycleScope.launch {
                    myMutex.withLock {
                        render2()
                    }
                }
            })
        }
    
        suspend fun render1() {
            Log.d("tag", "render1 start")
            delay(1000)
            Log.d("tag", "render1 end")
        }
    
        suspend fun render2() {
            Log.d("tag", "render2 start")
            delay(1000)
            Log.d("tag", "render2 end")
        }
    
    }
    

    【讨论】:

      【解决方案4】:

      .launch{} 返回一个Job,所以你可以保存引用,然后:

          suspend fun render2() {
              render1Job.join()
              do rendering
          }
      

      launchJobjoin

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-23
        • 2018-11-29
        • 2020-11-30
        • 1970-01-01
        相关资源
        最近更新 更多