Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to use Coroutine in kotlin to call a function every second

i just created an app where my function getdata() call every second to fetch new data from server and updateui() function will update view in UI i don't use any asynctask or coroutine in my app i wants to do this please tell me how i can do that.

here's my code...

private fun getdata(){
        try {
            val api = RetroClient.getApiService()
            call = api.myJSON
            call!!.enqueue(object : Callback<ProductResponse> {
                override fun onResponse(
                    call: Call<ProductResponse>,
                    response: Response<ProductResponse>
                ) {
                    if (response.isSuccessful) {
                        productList = response.body()!!.data
                        for (list in productList) {
                            if (list.BB.equals("AAA")) {
                                aProductList.add(list)
                            }
                        }
                        if (recyclerView.adapter != null) {
                            eAdapter!!.updatedata(aProductList)
                        }
                        updateui()
                    }
                }

                override fun onFailure(call: Call<ProductResponse>, t: Throwable) {
                    println("error")
                }
            })
        } catch (ex: Exception) {
        } catch (ex: OutOfMemoryError) {
        }
Handler().postDelayed({
            getdata()
        }, 1000)
}


private fun updateui() {
        try {
            //some code to handel ui
 } catch (e: NumberFormatException) {

        } catch (e: ArithmeticException) {

        } catch (e: NullPointerException) {

        } catch (e: Exception) {

        }
    }
like image 703
user12575366 Avatar asked Mar 13 '20 14:03

user12575366


People also ask

Do coroutines run concurrently?

Coroutines can be executed concurrently using a multi-threaded dispatcher like the Dispatchers.

How does coroutine delay work?

delay is a special suspending function. It suspends the coroutine for a specific time. Suspending a coroutine does not block the underlying thread, but allows other coroutines to run and use the underlying thread for their code.

How do you wait in a coroutine?

To wait for a coroutine to finish, you can call Job. join . join is a suspending function, meaning that the coroutine calling it will be suspended until it is told to resume. At the point of suspension, the executing thread is released to any other available coroutines (that are sharing that thread or thread pool).


5 Answers

To run a function every second with coroutines:

val scope = MainScope() // could also use an other scope such as viewModelScope if available
var job: Job? = null

fun startUpdates() {
    stopUpdates()
    job = scope.launch {
        while(true) {
            getData() // the function that should be ran every second
            delay(1000)
        }
    }
}

fun stopUpdates() {
    job?.cancel()
    job = null
}

However, if getData() only starts a network request and doesn't wait for its completion, this might not be a very good idea. The function will be called a second after it finished, but because the network request is done asynchronously it may be scheduled way too much.
For example if the network request takes 5 seconds, it will have been started 4 more times before the first one even finished!

To fix this, you should find a way to suspend the coroutine until the network request is done.
This could be done by using a blocking api, then pass Dispatchers.IO to the launch function to make sure it's done on a background thread.

Alternatively you could use suspendCoroutine to convert a callback-based api to a suspending one.


Update - Lifecycle scope

Inside a component with a Android Lifecycle you could use the following code to automate repeating ui updates:

fun startUpdates() {
    val lifecycle = this // in Activity
    val lifecycle = viewLifecycleOwner // in Fragment

    lifecycle.lifecycleScope.launch {
        lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
            // this block is automatically executed when moving into
            // the started state, and cancelled when stopping.
            while (true) {
                getData() // the function to repeat
                delay(1000)
            }
        }
    }
}

This code requires the current androidx.lifecycle:lifecycle-runtime-ktx dependency.

The above remark about async, blocking or suspending code inside getData() still applies.

like image 90
RobCo Avatar answered Oct 16 '22 19:10

RobCo


it's not advisable to hit the server every second. if you need to get data continuously try the socket. Because some times your server takes more than a few seconds to respond to your request. Then all your requests will be in a queue..if you still need to try with this.

fun repeatFun(): Job {
    return coroutineScope.launch {  
        while(isActive) {
            //do your network request here
            delay(1000)
        }
    }
}

//start the loop
val repeatFun = repeatRequest()

//Cancel the loop
repeatFun.cancel()
like image 34
vignesh Avatar answered Oct 16 '22 20:10

vignesh


For those who are new to Coroutine

add Coroutine in Build.gradle

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'

To create a repeating Job

    /**
     * start Job
     * val job = startRepeatingJob()
     * cancels the job and waits for its completion
     * job.cancelAndJoin()
     * Params
     * timeInterval: time milliSeconds 
     */
    private fun startRepeatingJob(timeInterval: Long): Job {
        return CoroutineScope(Dispatchers.Default).launch {
            while (NonCancellable.isActive) {
                // add your task here
                doSomething()
                delay(timeInterval)
            }
        }
    }

To start:

  Job myJob = startRepeatingJob(1000L)

To Stop:

    myJob .cancel()
like image 23
Hitesh Sahu Avatar answered Oct 16 '22 20:10

Hitesh Sahu


I ended up doing like this with an extension function:

fun CoroutineScope.launchPeriodicAsync(repeatMillis: Long, action: () -> Unit) = this.async {
  while (isActive) {
    action()
    delay(repeatMillis)
  }
}

then call it like:

val fetchDatesTimer = CoroutineScope(Dispatchers.IO)
  .launchPeriodicAsync(TimeUnit.MINUTES.toMillis(1)) {
    viewModel.fetchDeliveryDates()
  }

and cancel it like:

fetchDatesTimer.cancel()
like image 5
nilsi Avatar answered Oct 16 '22 21:10

nilsi


My solution in Kotlin inside MainViewModel

fun apiCall() {
       viewModelScope.launch(Dispatchers.IO) {
         while(isActive) {
            when(val response = repository.getServerData()) {
                is NetworkState.Success -> {
                    getAllData.postValue(response.data)
                }
                is NetworkState.Error -> [email protected] = false
            }

            delay(1000)
        }
    }
}


sealed class NetworkState<out R> {
    data class Success<out T>(val data: T): NetworkState<T>()
    data class Error(val exception: String): NetworkState<Nothing>()
    object Loading: NetworkState<Nothing>()
}
like image 3
Thiago Avatar answered Oct 16 '22 21:10

Thiago