0

I have a need to update the UI from a thread. In doing so, I created a Toast message to test that the function was working as expected. While in my thread, I run the function "runOnUiThread" and inside this function I place a Toast message, like this...

runOnUiThread {
    Toast.makeText(this, "DIALOG Start", Toast.LENGTH_SHORT).show()
}

Although this does work, it also doesn't work. The Toast message stays on the device screen. I have tested it both on emulator and live device. Every time I run the code, the Toast message stays on the screen.

However, if I separate the toast message as an extension type function, it works as expected.

val toast = Toast.makeText(this@MainActivity, "Dialog finish", Toast.LENGTH_SHORT)

runOnUiThread {
    toast.show()
}

This works as expected.

I am wondering, why? Both sets of code call runOnUiThread(). Both are calling a Toast message. What is the difference in the two?

Is this a product of using Kotlin and updating UI and updating from a Thread? Is this because the thread is not stopped? Will the Toast go away if I call

thread.stop() Although it is deprecated.

This is the code that works:

progressDoalog!!.max = 100
val toast = Toast.makeText(this@MainActivity, "Dialog finish", Toast.LENGTH_SHORT)
Thread {
   try {
        while (progressDoalog!!.progress <= progressDoalog!!.max) {
        Thread.sleep(50)
        handle.sendMessage(handle.obtainMessage())
        if (progressDoalog!!.progress == progressDoalog!!.max) {
        progressDoalog!!.dismiss()
        runOnUiThread {
           toast.show()
        }
    }
  }
 } catch (e: Exception) {
       e.printStackTrace()
 }
}.start()
EPAgg
  • 99
  • 1
  • 12
  • I don't know why the two variants are behaving differently, but for the former I'd assume that it doesn't stay, just that it gets called again and again (maybe put a log statement in there and check your console?). Do you really want a `<=`comparison as your loop condition? I don't think `progress` can ever be greater than `max`? – Beko Feb 22 '21 at 23:34
  • You are looping the toast show stuff, that's why you got the toast stays on your screen – glzlaohuai Feb 23 '21 at 04:07
  • If I use it as an extension function, it doesn't stay. Additionally, it will stay well past the dialog.dismiss. – EPAgg Feb 23 '21 at 13:47
  • The while loop shouldn't matter and should fire the Toast once as soon as progress == max. – EPAgg Feb 23 '21 at 13:48
  • Unrelated to what you're asking, but you also need to watch out for leaking your activity/fragment. If the user closes the activity or fragment before your thread is done, you need to set some condition that will complete the thread, or it will keep running, waiting for the dialog progress to complete, which will leak the activity or fragment. – Tenfour04 Feb 23 '21 at 15:09

1 Answers1

3

The cause of the problem is more clear if you fix your indentation:

val toast = Toast.makeText(this@MainActivity, "Dialog finish", Toast.LENGTH_SHORT)
Thread {
    try {
        while (progressDoalog!!.progress <= progressDoalog!!.max) {
            Thread.sleep(50)
            handle.sendMessage(handle.obtainMessage())
            if (progressDoalog!!.progress == progressDoalog!!.max) {
                progressDoalog!!.dismiss()
                runOnUiThread {
                    toast.show()
                }
            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}.start()

This while loop runs forever. Once progress is equal to max, you start showing the toast over and over. Your conditional for the while loop will still be true forever, so it never stops calling show() on your Toast.

In your non-working code, you were creating a new Toast on every iteration of the loop, so once one toast goes away, there's another identical one behind it.

In your "working" code, you are calling show() on the same Toast instance over and over, so it only appears once. However, you will have leaked an immortal thread.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • Apologies, I didn't include the line. progressdoalog!!.max = 100 here, but it is in my code. The loop stops when the max is reached. The problem I have is that the Toast stays visible. if I put the entire toast message in the runOnUiThread. It works as expected if I call the toast and .show(). In both instances, I call .show() within that loop that counts to 100. Why does it remain under one instance and not the other?? – EPAgg Feb 23 '21 at 13:55
  • It doesn't remain in either instance. You are continually showing more and more copies of it. If you have only one instance, calling show() multiple times doesn't repeatedly show it, so it appears to behave correctly. Your code as posted will loop forever because the dialog `progress` will never exceed `max` It doesn't matter if you set `max` to 1 or 100, because `ProgressDialog.progress` will always be less than or equal to `max`. – Tenfour04 Feb 23 '21 at 14:09
  • That make no since. if there is a max... 100 the loop will continue until it reaches the max 100. If what you say is true, then when max reaches any number between 1 and 100, the dialog would dismiss. But, the dialog will dismiss in both instances. In the second instance the toast shows as expected and goes away. – EPAgg Feb 23 '21 at 14:29
  • There is nothing that breaks your while loop. When the dialog progress reaches 100, `while (100 <= 100)` is still satisfied. Dismissing the dialog doesn't stop the while loop from continuing to run. – Tenfour04 Feb 23 '21 at 15:05
  • yes, I see what you are saying. The while loop is still looping because it is never complete given the <=. That should only be a < so the loop is satisfied at max. Thank you for pointing that out. – EPAgg Feb 23 '21 at 17:29