Kotlin: How to delay code in Android without making the UI freeze
Asked Answered
L

8

24

I am trying to delay code in Kotlin I have tried

Thread.sleep(1000)

But its freezes the UI.

Does somebody know why this is happening And how to delay without freezing the UI?

Lh answered 24/1, 2019 at 14:18 Comment(1)
do the answers solve your problem? :)Suttle
D
35

What went wrong

Usage Thread.sleep(...)

Thread.sleep causes the current thread to suspend execution for a specified period. This is an efficient means of making processor time available to the other threads of an application or other applications that might be running on a computer system.

For the OP (Original Poster / Asker)'s clarification:

It freezes the UI, does somebody know why this is happening?

As mentioned above from the official documentation of Java, you are experiencing a some sort of freezing in the UI because you have called it in the Main Thread.

Main Thread or if you are doing your stuff in Android, it is often called the UI Thread:

On the Android platform, applications operate, by default, on one thread. This thread is called the UI thread. It is often called that because this single thread displays the user interface and listens for events that occur when the user interacts with the app.

Without using the help of multi-threading APIs (Such as Runnable, Coroutines, RxJava), you will automatically be invoking Thread.sleep(1000) on the UI Thread that is why you are experiencing such "UI Freezing" experience because, other UI Operations are blocked from accessing the thread since you have invoke a suspension on it.

And how to delay without freezing the ui?

Harness the power of available APIs for multi-threading, so far it's good to start with the following options:

1. Runnable

In Java

// Import
import android.os.Handler;

// Use
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
     // do something after 1000ms
  }
}, 1000);

In Kotlin

// Import
import android.os.Handler;

// Use
val handler = Handler()
handler.postDelayed({
     // do something after 1000ms
}, 1000)

2. Kotlin Coroutines

// Import
import java.util.*
import kotlin.concurrent.schedule

// Use
Timer().schedule(1000){
    // do something after 1 second
}

3. RxJava

// Import
import io.reactivex.Completable
import java.util.concurrent.TimeUnit

// Use
Completable
     .timer(1, TimeUnit.SECONDS)
     .subscribeOn(Schedulers.io()) // where the work should be done
     .observeOn(AndroidSchedulers.mainThread()) // where the data stream should be delivered
     .subscribe({
          // do something after 1 second
     }, {
          // do something on error
     })

Amongst the three, currently, RxJava is the way to go for multi threading and handling vast amount of data streams in your application. But, if you are just starting out, it is good to try out the fundamentals first.

References

Duffel answered 24/1, 2019 at 17:33 Comment(2)
Hi asaf, let me know if this helps.Duffel
I got a deprecation warning using Handler as suggested: developer.android.com/reference/kotlin/android/os/Handler#initRunning
E
8

You can use Handler object https://developer.android.com/reference/android/os/Handler.

val handler = Handler()
val runnable = Runnable {
    // code which will be delayed  
}

handler.postDelayed(runnable, 1000)

1000 is time in miliseconds, you should replace it with your value.

Etui answered 24/1, 2019 at 14:37 Comment(0)
C
7

If you don't want to freeze the UI, you need to execute your code off of the MainThread.
There are a lot of way of doing it. Some examples:

Thread

Thread {
    Thread.sleep(1000)
    // Your code
}.start()

Rx

You need https://github.com/ReactiveX/RxJava

Flowable.timer(1000, TimeUnit.MILLISECONDS)
    .subscribeOn(AndroidSchedulers.mainThread())
    .subscribe { 
        // Your code
    }

Kotlin coroutine

GlobalScope.launch { // launch new coroutine in background and continue
    delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
    println("World!") // print after delay
}

reference: https://kotlinlang.org/docs/reference/coroutines-overview.html

Documentations:

Colcothar answered 24/1, 2019 at 14:35 Comment(2)
Maybe worth starting by pointing out that the OP's code is running on the ‘main thread’ (does Android call that the Event-Dispatch Thread like Swing does?), and explaining why?Allina
I tried the kotlin coroutine but its does a compile error is there a library that i should use?Lh
S
4

if you are looking at delaying the code on a background thread and update the UI later kotlin coroutines come in handy check below

  Timer().schedule(2000){
        CoroutineScope(Main).launch {
            withContext(Main){
                //run UI related code 
                //will be executed after the timeout
            }
        }

    }
Schleiermacher answered 16/12, 2021 at 9:5 Comment(0)
C
2
GlobalSocpe.launch(Dispatchers.MAIN){
delay(5000)
}

this is the code part you asked for. But for your solution, first understand

launch

and

async

Similarly you should understand other values of

Dispatchers

Chak answered 21/4, 2020 at 18:38 Comment(2)
Instead of delay() function if I use Thread.sleep(6000) will it block the main thread?Chlorella
it might not block the main thread but will block the thread where it is running.Chak
D
1

Kotlin

Delayed code on UI Thread with Coroutines from a Fragment

Timer().schedule(1000) {

    activity?.runOnUiThread {

        // do something after 1000ms

        }
}

If you get this Exception

java.lang.IllegalStateException: Method setCurrentState must be called on the main thread

Handler is Deprecated

Decani answered 24/11, 2021 at 8:18 Comment(0)
E
0

Thread.sleep(1000);

Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, [...]

see https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#sleep(long)

If you call this on the UI thread, which is the default thread your code runs on, it will stop and wait.

In pure Kotlin you should use a coroutine:

import kotlinx.coroutines.*

fun main() {
    GlobalScope.launch { // launch new coroutine in background and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println("World!") // print after delay
    }
    println("Hello,") // main thread continues while coroutine is delayed
    Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
}

If you are programming an Android app you can use a Handler and Androidx for especially nice syntax:

Handler().postDelayed( 1000 ) { doSomething() }
Ensure answered 24/1, 2019 at 15:26 Comment(0)
N
0

Use this extension:

//extension:
fun GlobalScope.runDelayedUITask(delay: Long, task: () -> Unit) {
    launch(Dispatchers.IO) {
        delay(delay)
        launch(Dispatchers.Main) {
            task()
        }
    }
}


//usage:
GlobalScope.runDelayedUITask(1000) {
    //Your task...
}


Nimble answered 23/4, 2022 at 15:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.