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?
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:
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 crashAnother 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.