Home > Net >  Is this the correct way to use coroutines?
Is this the correct way to use coroutines?

Time:02-25

I have made a sample app which updates a piece of UI (a textview) after running a coroutine. I have very little exposure to coroutines and stuff, so I would like to get a feedback on whether my approach is correct or not, and if there is something I can add/modify to have better code writing practices. Thank you.

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import com.example.coroutinesandui.databinding.ActivityMainBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private var count = 0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.counterTextView.text = "0"
        binding.buttonIncrement.setOnClickListener {
            Toast.makeText(binding.buttonIncrement.context,"Increasing counter",Toast.LENGTH_SHORT).show()
            lifecycleScope.launch {
                updateUI()
            }
        }
    }
    private suspend fun updateUI() {
        count  
        delay(5000)  // simulating database/network operation
        withContext(Dispatchers.Main) {
            binding.counterTextView.text = count.toString()
        }
    }
}

CodePudding user response:

This approach is correct, provided that when you replace delay() with your actual work that you wrap that work either in a suspend function that delegates to an appropriate dispatcher, or wrap the work in a withContext call that delegates to an appropriate dispatcher.

If the work is rather lengthy, more than a few seconds like a big download from the network, you would probably want to move it to the ViewModel. You could have the ViewModel return a Deferred that can be await()ed in your Activity's suspend function, and return that same Deferred instance if the Activity (which might be a new instance after a screen rotation) asks for it again rather than restarting the job.

CodePudding user response:

When you launch coroutine in the Main threadlifecycleScope.launch {}, you no need to change the coroutine context to update UI, because coroutine is already running in the context of Main thread.

 private suspend fun updateUI() {
        count  
        delay(5000)  // simulating database/network operation
        binding.counterTextView.text = count.toString()
            }

If you start coroutine in the background thread lifecycleScope.launch(Dispatchers.Default) {} ,you have to change context to update UI using withContext(Dispatchers.Main)

If you think background thread is not needed for simple task ,you can launch coroutine using async{} builder to run task concurrently .So Main thread can do some other task while delay

 private suspend fun getCount():Int{
            count  
            delay(5000)
            return count }

val countDefered = async { getCount() }.await()
binding.counterTextView.text = countDefered.toString()

You can check whether the coroutine running in Main thread or some other thread using Thread.currentThread().name

lifecycleScope.launch {
            Log.d("Thread name",Thread.currentThread().name)//prints main

}

  • Related