简体   繁体   中英

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

Is there any way to update "livedata2" after rendering is completed of "livedata1"?

(I have to use ViewModel and Livedata also)

It looks like you do not need to use lifecycleScope.launch and all will work fine as you intended.

  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.

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:

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

See launch , Job , join

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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