android - Is this the correct way to use coroutines? - TagMerge
5Is this the correct way to use coroutines?Is this the correct way to use coroutines?

Is this the correct way to use coroutines?

Asked 1 years ago
0
5 answers

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.

Source: link

0

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

}

Source: link

0

For the demonstration of the project, we will be using the following three dependencies:
//For using viewModelScopeimplementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0"//For runBlockingTest, CoroutineDispatcher etc.testImplementation “org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2”//For InstantTaskExecutorRuletestImplementation “androidx.arch.core:core-testing:2.1.0”
If we try to run the Unit test in the Dispatcher.Main thread, our test will fail with the following error:
Exception in thread "main @coroutine#1" java.lang.IllegalStateException: Module with the Main dispatcher had failed to initialize. For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used
It looks like a perfect test to proceed. But if we run the Unit test, there will be an error:
Exception in thread "main @coroutine#2" java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details. at android.os.Looper.getMainLooper(Looper.java)

Source: link

0

In scripting, using Update based functions, it would look something like this:
// Please don't do this
bool tankMoving;
bool facingRightWay; 
bool tankInPosition;
float timer;
void Update()
{
     if(Input.GetMouseButtonDown(0))
        {
            tankMoving = true;
        }
     if (tankMoving)
        {
            MoveTank();
        }
}
void MoveTank()
    {
        if (facingRightWay == false)
        {
            if (angleIsWrong)
            {
                TurnTank();
            }
            else if (angleIsCorrect)
            {
                facingRightWay = true;
            }
        }
        if (facingRightWay && tankInPosition == false)
        {
            if (positionIsWrong)
            {
                MoveToPosition();
            }
            else if (positionIsCorrect)
            {
                tankInPosition = true;
            }
        }
        if (facingRightWay && tankInPosition && timer < 1) 
        {
            timer += Time.deltaTime;
        }
        else if (facingRightWay && tankInPosition && timer > 1)
        {
           FireTank();
           facingRightWay = false;
           tankInPosition = false;
           tankMoving = false;
           timer = 0;
        }
    }
Here’s the same logic again, this time inside a Coroutine:
void Update()
{
    if(Input.GetMouseButtonDown(0))
    {
        StartCoroutine(MoveTank());
    }
}
IEnumerator MoveTank()
    {
        while(facingWrongWay)
        {
            TurnTank();
            yield return null;
        }
        while (notInPosition)
        {
            MoveToPosition();
            yield return null;
        }
        yield return new WaitForSeconds(1);
        Fire();    
    }
First, the return type of a Coroutine needs to be IEnumerator, like this:
IEnumerator MyCoroutine()
{
    // Code goes here!
}
Just like normal functions, you can pass parameters into a Coroutine.
IEnumerator MyCoroutine(int number)
{
    number++;
    // Code goes here.
}
Combining yield return null with a while Loop creates mini Update Loops. Like this.
IEnumerator MyCoroutine()
    {
        int i = 0;
        while (i < 10)
        {
            // Count to Ten
            i++;
            yield return null;
        }
        while (i > 0)
        {
            // Count back to Zero
            i--;
            yield return null;
        }
        // All done!
    }

Source: link

0

Here is a simple example of how coroutines can be useful. Suppose you have a consumer-producer relationship where one routine creates items and adds them to a queue and another removes items from the queue and uses them. For reasons of efficiency, you want to add and remove several items at once. The code might look like this:
var q := new queue

coroutine produce
    loop
        while q is not full
            create some new items
            add the items to q
        yield to consume

coroutine consume
    loop
        while q is not empty
            remove some items from q
            use the items
        yield to produce

call produce
However, it is still possible to implement coroutines on top of a generator facility, with the aid of a top-level dispatcher routine (a trampoline, essentially) that passes control explicitly to child generators identified by tokens passed back from the generators:
var q := new queue

generator produce
    loop
        while q is not full
            create some new items
            add the items to q
        yield consume

generator consume
    loop
        while q is not empty
            remove some items from q
            use the items
        yield produce

subroutine dispatcher
    var d := new dictionary(generator → iterator)
    d[produce] := start produce
    d[consume] := start consume
    var current := produce
    loop
        call current
        current := next d[current]

call dispatcher

Source: link

Recent Questions on android

    Programming Languages