简体   繁体   English

如何在 ViewModel 和 LiveData 之间启动和执行一个协程

[英]How launch and execute just one coroutine between ViewModel and LiveData

Here is my pseudo code.这是我的伪代码。

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

}

Everytime I call "launch", It seems that a new coroutine started.每次我调用“启动”时,似乎都启动了一个新的协程。

I want to wait updating "livedata2" until end of "livedata1" rendering我想等待更新“livedata2”直到“livedata1”渲染结束

Is there any way to update "livedata2" after rendering is completed of "livedata1"?在“livedata1”渲染完成后,有什么方法可以更新“livedata2”?

(I have to use ViewModel and Livedata also) (我也必须使用 ViewModel 和 Livedata)

It looks like you do not need to use lifecycleScope.launch and all will work fine as you intended.看起来您不需要使用lifecycleScope.launch并且一切都会按您的预期正常工作。

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

Also you can launch coroutine on Main thread:您也可以在主线程上启动协程:

  lifecycleScope.launch(Dispatchers.Main) {
            render1() 
       }
    suspend fun render1() {
        Log.d("tag", "render1 start")
        delay(1000)
        liveData2.postValue("render2")
        Log.d("tag", "render1 end")
    }

Since you're launching coroutines inside observers, they're gonna run concurrently despite they're confined to a single thread (Main).由于您在观察者内部启动协程,因此尽管它们被限制在单个线程(主线程)中,它们仍将同时运行。 When delay is called, the main thread gets suspended instead of blocked, that's why it runs the other task during that time.当调用delay时,主线程被挂起而不是被阻塞,这就是它在那段时间运行其他任务的原因。

To avoid running both render functions concurrently you're gonna have to deal with concurrency, for example using mutual exclusion:为了避免同时运行两个渲染函数,您将不得不处理并发,例如使用互斥:

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

}

.launch{} returns a Job , so you can save out the reference, then: .launch{}返回一个Job ,因此您可以保存引用,然后:

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

See launch , Job , join启动工作加入

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM