Search code examples
androidkotlinandroid-layoutsleep

How to use Thread.sleep() in Kotlin


This comes from near the end of the codelab found here:

Intro to debugging - Debugging example: accessing a value that doesn't exist

This is all inside the MainActivity.kt file

Here's my onCreate

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val helloTextView: TextView = findViewById(R.id.division_textview)
    helloTextView.text = "Hello, debugging!"

    division()
}
//...

Here's the division function provided by Google, but with 2 changes I'll explain in a moment...

fun division() {
  val numerator = 60
  var denominator = 4
    repeat(4) {
     Thread.sleep(3000)
     findViewById<TextView>(R.id.division_textview).setText("${numerator / denominator}")
     Log.v(TAG, "${numerator / denominator}")
     denominator--
    }
  }

The instructions make it seem like they expect sleep() to accept seconds, but AS indicates it expects millis, so I changed their 3 to 3000. I added Log.v(TAG, "${numerator / denominator}") to see what was happening because it wasn't doing as expected.

The point of this was to have the emulator create a gif of the quotients being updated. This is supposed to be helpful when debugging.

Nothing displays on screen, not even the app's name, until the repeat() finishes. The logs happen in 3 second intervals, as expected.

Why is the layout waiting, and how do I make it update on each iteration of the loop?


Solution

  • I honestly have no idea what that Codelab is doing, based off the code they provide. The app isn't going to render anything (not the layout, not any changes to the layout) before onCreate finishes, and onCreate won't finish until it's run all its code, including that repeat block in the division function it calls.

    division isn't starting any worker threads, so all Thread.sleep is doing is blocking the main thread - it's hanging the app. And you're right, sleep does take a millis value, not seconds - I get the feeling they didn't actually run this code, it's full of other mistakes and inconsistencies that honestly made it hard to work out what you were meant to be doing. Change which Log.d call? The ones in onCreate? (They actually mean the Log.v call in division, I assume)


    Here's how you'd use a thread in Kotlin - you need to create a new one (so you're off the main thread, so it can actually finish creating the activity and run the UI):

    fun division() {
        // create a new thread (and start it immediately)
        thread(start=true) {
            repeat(4) { i ->
                Thread.sleep(3000L)
                // assuming you've done the ``findViewById`` and assigned it to a variable
                runOnUiThread { divisionTextView.text = "$i" }
            }
        }
    }
    

    That's just updating with the current repeat number (i) for brevity, but the important things are:

    • you're creating a new thread to do the work (and sleeping)
    • you're using runOnUiThread (which is a method on the Activity) to do the text updating on the main/UI thread (same thing). If you try to touch the UI from another thread it will crash

    Another way you can do that is to post a runnable to the UI thread through a view, may as well use that TextView:

    divisionTextView.post { divisionTextView.text = "$i" }
    

    Coroutines would be a better idea here (you can run them on the main thread without it blocking, so you don't have to worry about switching threads to update the UI, or any thread safety stuff) but that's the basics of doing a thread. Genuinely have no idea what's going on in that codelab.