How to call a function after delay in Kotlin? - android

As the title, is there any way to call a function after delay (1 second for example) in Kotlin?

There is also an option to use Handler -> postDelayed
Handler().postDelayed({
//doSomethingHere()
}, 1000)

Many Ways
1. Using Handler class
Handler().postDelayed({
TODO("Do something")
}, 2000)
2. Using Timer class
Timer().schedule(object : TimerTask() {
override fun run() {
TODO("Do something")
}
}, 2000)
// Shorter
Timer().schedule(timerTask {
TODO("Do something")
}, 2000)
// Shortest
Timer().schedule(2000) {
TODO("Do something")
}
3. Using Executors class
Executors.newSingleThreadScheduledExecutor().schedule({
TODO("Do something")
}, 2, TimeUnit.SECONDS)

You can use Schedule
inline fun Timer.schedule(
delay: Long,
crossinline action: TimerTask.() -> Unit
): TimerTask (source)
example (thanks #Nguyen Minh Binh - found it here: http://jamie.mccrindle.org/2013/02/exploring-kotlin-standard-library-part-3.html)
import java.util.Timer
import kotlin.concurrent.schedule
Timer("SettingUp", false).schedule(500) {
doSomething()
}

You could launch a coroutine, delay it and then call the function:
/*GlobalScope.*/launch {
delay(1000)
yourFn()
}
If you are outside of a class or object prepend GlobalScope to let the coroutine run there, otherwise it is recommended to implement the CoroutineScope in the surrounding class, which allows to cancel all coroutines associated to that scope if necessary.

You have to import the following two libraries:
import java.util.*
import kotlin.concurrent.schedule
and after that use it in this way:
Timer().schedule(10000){
//do something
}

val timer = Timer()
timer.schedule(timerTask { nextScreen() }, 3000)

If you're using more recent Android APIs the Handler empty constructor has been deprecated and you should include a Looper. You can easily get one through Looper.getMainLooper().
Handler(Looper.getMainLooper()).postDelayed({
//Your code
}, 2000) //millis

If you are in a fragment with viewModel scope you can use Kotlin coroutines:
myViewModel.viewModelScope.launch {
delay(2000)
// DoSomething()
}

A simple example to show a toast after 3 seconds :
fun onBtnClick() {
val handler = Handler()
handler.postDelayed({ showToast() }, 3000)
}
fun showToast(){
Toast.makeText(context, "Its toast!", Toast.LENGTH_SHORT).show()
}

If you are looking for generic usage, here is my suggestion:
Create a class named as Run:
class Run {
companion object {
fun after(delay: Long, process: () -> Unit) {
Handler().postDelayed({
process()
}, delay)
}
}
}
And use like this:
Run.after(1000, {
// print something useful etc.
})

i suggest to use kotlin coroutine and if you want to cancel it. its simple and light weight.
fun repeatFun(): Job {
return coroutineScope.launch {
while(isActive) {
//do your work here
delay(1000)
}
}
}
//start the loop
val repeatFun = repeatRequest()
//Cancel the loop
repeatFun.cancel()

I recommended using SingleThread because you do not have to kill it after using. Also, "stop()" method is deprecated in Kotlin language.
private fun mDoThisJob(){
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate({
//TODO: You can write your periodical job here..!
}, 1, 1, TimeUnit.SECONDS)
}
Moreover, you can use it for periodical job. It is very useful. If you would like to do job for each second, you can set because parameters of it:
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
TimeUnit values are: NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS.

I use the following function(s):
fun <T> delayedAsyncTask(delayMs: Long = 0, task: () -> T): ScheduledFuture<T> {
return Executors
.newSingleThreadScheduledExecutor()
.schedule(Callable<T> { task() }, delayMs, TimeUnit.MILLISECONDS)
}
fun <T> asyncTask(task: () -> T) = delayedAsyncTask(0, task)
Here's a unit test for the delayed function. Use of the returned future is optional of course.
#Test
fun `delayed task work correctly`() {
val future = delayedAsyncTask(1000) {
"lambda returns hello world"
}
assertEquals(false, future.isDone)
Thread.sleep(1100)
assertEquals(true, future.isDone)
assertEquals("lambda returns hello world", future.get())
}

Another way to create a redundant job other than this; that does not require the function to be suspend.
val repeatableJob = CoroutineScope(Dispatchers.IO).launch {
while (true) {
delay(1000)
}
}
Cancel when you are done -
repeatableJob.cancel()

Related

how to avoid using GlobalScope

In my code, I have a time-out functionality and I want to use a countdown timer but after a lot of research, I couldn't find similar functionality as a countdown timer in Kotlin coroutine (able to start, cancel and catch finish callback). Then I decided to use GlobalScope.launch. I know this is a bad solution but my code is working perfectly.
Here is my code
viewModelScope.launch {
val timer = object: CountDownTimer(Constants.PAYMENT_TIMER, 1000) {
override fun onTick(millisUntilFinished: Long) {}
override fun onFinish() {
GlobalScope.launch {
_eventFlow.emit(UIPaymentEvent.NavigateToBack)
}
}
}
timer.start()
collectPaymentIntentUseCase.invoke(currentPaymentIntent!!).onEach { result ->
when (result) {
is Resource.Success -> {
timer.cancel()
if (result.data?.exception == null) {
My question is how can find a 100% similar function to avoid using GlobalScope but be able to use the countdown timer (start, cancel,onComplete callback)?
Note: I am using GlobalScope.lanch to be able to emit UIPaymentEvent.NavigateToBack event to my view
You don't need a CountDownTimer here. Just use the delay() suspend function.
viewModelScope.launch {
val job = launch {
delay(Constants.PAYMENT_TIMER) // Wait for timeout
_eventFlow.emit(UIPaymentEvent.NavigateToBack)
}
collectPaymentIntentUseCase.invoke(currentPaymentIntent!!).onEach { result ->
when (result) {
is Resource.Success -> {
job.cancel() // Cancel the timer
if (result.data?.exception == null) {
You can use callbackFlow for listen your timer. I just code this editor. I hope it will be helpful.
fun timerFlow() = callbackFlow<UIPaymentEvent> {
val timer = object : CountDownTimer(10, 1000) {
override fun onTick(millisUntilFinished: Long) {}
override fun onFinish() {
CoroutineScope().launch {
_eventFlow.emit(UIPaymentEvent.NavigateToBack)
}
}
}
timer.start()
awaitClose()
}
Coroutines are launched inside a CoroutineScope which are similar to lifecycle for android. As such, Android automatically provide coroutine's scope for components like activity or fragment and bound them to theirs lifecycle.
While it's not recommended to use the global scope that starts and ends with android's process. There are no restriction on creating your own and limiting it to a specific view of time. Creating one starts its life and cancelling it stops all tasks inside.
In your case a countdown can be done with only coroutines. As stated in this answer.
But without changing too much of your existing code you could reuse the viewModelScope that launched your timer to emit your event.
viewModelScope.launch {
_eventFlow.emit(UIPaymentEvent.NavigateToBack)
}
Beware of the life of your scope. If the viewmodelScope is dead when the timer finish, the event will never be sent.

Timer based on Handler stops, Android, Kotlin

I use Handler for creating a timer in a Widget.
I use the recommended constructor, i.e. passing a Looper to it.
private val updateHandler = Handler(Looper.getMainLooper())
#RequiresApi(Build.VERSION_CODES.Q)
private val runnable = Runnable {
updateDisplay()
}
#RequiresApi(Build.VERSION_CODES.Q)
private fun updateDisplay () {
updateHandler?.postDelayed(runnable, TIMER_MS)
// some other code
}
The TIMER MS is set to 3000 ms.
The timer runs fine for a while and execute the given code. However after a random time elapsed the timer stops working and no more execution of the given code happens.
Please advise what the problem could be ond how to fix it.
Alternatively, can I use some other timer? (The timer should go off every few second - this is the reason why I use Handler)
Thank you for any advice in advance
You could always try using a Coroutine for something like this:
class TimedRepeater(var delayMs: Long,
var worker: (() -> Unit)) {
private var timerJob: Job? = null
suspend fun start() {
if (timerJob != null) throw IllegalStateException()
timerJob = launch {
while(isActive) {
delay(delayMs)
worker()
}
}
}
suspend fun stop() {
if (timerJob == null) return
timerJob.cancelAndJoin()
timerJob = null
}
}
suspend fun myStuff() {
val timer = Timer(1000) {
// Do my work
}
timer.start()
// Some time later
timer.stop()
}
I haven't tested the above, but it should work well enough.
You can use CountDownTimer from Android framework to achieve the same. It internally uses Handler for timer
val timer = object: CountDownTimer(1000,1000){
override fun onTick(millisUntilFinished: Long) {
}
override fun onFinish() {
}
}
timer.start()

How to set delay in Kotlin?

I just want to wait 2 or 3 seconds, I know how to do it in Java and I tried it, but no idea about kotlin
something simple like:
println("hello")
// a 2 seconds delay
println("world")
It's simple. Just use a coroutine. Like this:
fun main() = runBlocking {
launch {
delay(2000L)
println("World!")
}
println("Hello")
}
Don't forget to import kotlin coroutine like this:
import kotlinx.coroutines.*
Happy coding in kotlin!!!
there are some ways:
1- use Handler(base on mili-second)(Deprecated):
println("hello")
Handler().postDelayed({
println("world")
}, 2000)
2- by using Executors(base on second):
println("hello")
Executors.newSingleThreadScheduledExecutor().schedule({
println("world")
}, 2, TimeUnit.SECONDS)
3- by using Timer(base on mili-second):
println("hello")
Timer().schedule(2000) {
println("world")
}
If you don't want to use any dependencies then you can use Thread.sleep(2000L)
Here, 1 second = 1000L
The code should look like this,
println("hello")
Thread.sleep(2000L)
println("world")
Use:
viewmodelScope.launch {
println("hello")
delay(2000)
println("world")
}

Repeating coroutine with delays

I am looking a way to launch a thread that would make a GUI element blink forever, in pseudo-code:
while (true) {
GUI element ON
wait for 1s
element OFF
wait for 1s
}
I'm thinking of a recursive chain of handlers, like this:
val handler = Handler()
handler.postDelayed({
Handler().postDelayed({ gui_element_on() }, 1000)
Handler().postDelayed({ gui_element_off() }, 1000)
handler.postDelayed(this,0)
}, 1000)
Only that this won't work. I don't know how to do it properly. And I am not sure if this is the most efficient way.
I've found this recursive solution:
val handler : Handler = Handler()
val run: Runnable = object : Runnable {
override fun run() {
gui_element_on()
handler.postDelayed({gui_element_off()}, 1000)
handler.postDelayed(this, 2000)
}
}
run.run()
It does the trick in this case.
you can use CoroutineScope.launch
Here, I use lifecycleScope extension property from Android Jetpack.
class MyFragment : Fragment() {
override fun onViewCreated(...) {
viewLifecycleOwner.lifecycleScope.launch {
while(isActive) {
GUIElementOn()
delay(1000)
GUIElementOff()
delay(1000)
}
}
}
}

Android Mockito Kotlin coroutine test cancel

I have a retrofit service
interface Service {
#PUT("path")
suspend fun dostuff(#Body body: String)
}
It is used in android view model.
class VM : ViewModel(private val service: Service){
private val viewModelJob = Job()
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
val state = MutableLiveData<String()
init {
uiScope.launch {
service.doStuff()
state.value = "lol"
}
}
override fun onCleared(){
viewModelJob.cancel()
}
}
I would like to write a test for the cancelling of the view model. This will be done mocking service and delaying so that the co routine does not complete. Whilst blocking, we invoke onCleared to cancel the co routine. This should prevent state getting set...
#Test
fun `on cleared - cancels request`() = runBlocking {
//given
`when`(service.doStuff()).thenAnswer { launch { delay(1000) } }
val vm = ViewModel(service)
// when
vm.cleared()
//then
assertThat(vm.state, nullValue())
}
However it seems that vm.state always gets set??? What is the best way to test when clearing a scope that a co routine gets cancelled?
The problem here is in thenAnswer { launch { delay(1000) } }, which effectively makes your doStuff method look like that:
suspend fun doStuff() {
launch { delay(1000) }
}
As you can see, this function does not actually suspend, it launches a coroutine and returns immediately. What would actually work here is thenAnswer { delay(1000) }, which does not work, because there is no suspend version of thenAnswer in Mockito (as far as I know at least).
I would recommend to switch to Mokk mocking library, which supports kotlin natively. Then you can write coEvery { doStuff() } coAnswers { delay(1000) } and it will make your test pass (after fixing all the syntax errors ofc).

Categories

Resources