0

I'm struggling to solve this problem when i use a custom toast message with Handler(Looper.getMainLooper()).post { message } to show it on screen. but It didn't work what i expected. In my case, i must use the combination.
(but I know if i don't use any Handler, it works well.)
So, What i want to do is to show the custom toast message and then finish a Activityimmediately.

First of all, I'll show you the resources.

custom_toast.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv_test_toast"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/ctm_bg_toast"
        android:gravity="center"
        android:padding="10dp"
        android:text="TextView"
        android:textColor="#FFFFFF"
        android:textSize="14dp" />

</LinearLayout>

ctm_bg_toast.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
    android:shape="rectangle">
    <solid android:color="#99000000"/>
    <corners android:radius="100dp"/>
    <padding android:left="10dp" android:top="7dp" android:right="10dp" android:bottom="7dp"/>
</shape>

and Please take a close look the following code snippet.

1. Using Handler

class TestActivity: AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        Handler(Looper.getMainLooper()).post {
            val toast = Toast(this@TestActivity)
            toast.view = View.inflate(this@TestActivity, R.layout.custom_toast, null)
            ((toast.view?.findViewById(R.id.tv_test_toast)) as TextView).text = "test message"
            toast.setGravity(Gravity.CENTER, 0, Toast.LENGTH_SHORT)
            toast.show()
        }
        finish()
    }
}

this code does not show the custom toast message. i feel like the reason might be that i just finish the Activity while Handler is working asynchronously.

but the second code snippet works well. but i have no idea how it works.

2. Using two layered Handler

class TestActivity: AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        Handler(Looper.getMainLooper()).post {
            Handler(Looper.getMainLooper()).post {
                val toast = Toast(this@TestActivity)
                toast.view = View.inflate(this@TestActivity, R.layout.custom_toast, null)
                ((toast.view?.findViewById(R.id.tv_test_toast)) as TextView).text = "test message"
                toast.setGravity(Gravity.CENTER, 0, Toast.LENGTH_SHORT)
                toast.show()
            }
        }
        finish()
    }
}

this code above is definitely working well. I just wrapped the Handler with another Handler. That's all!
So, What i want to know is how the two layered Handlers work well under the hood and What am i missing now?

CodingBruceLee
  • 657
  • 1
  • 5
  • 19

1 Answers1

1

It's almost definitely an issue with finish(). But there's a much simpler fix -- just run finish() inside the handler, right after you call toast.show()

class TestActivity: AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        Handler(Looper.getMainLooper()).post {
                val toast = Toast(this@TestActivity)
                toast.view = View.inflate(this@TestActivity, R.layout.custom_toast, null)
                ((toast.view?.findViewById(R.id.tv_test_toast)) as TextView).text = "test message"
                toast.setGravity(Gravity.CENTER, 0, Toast.LENGTH_SHORT)
                toast.show()
                finish()
            }
        }
}
user496854
  • 6,461
  • 10
  • 47
  • 84
  • Thank you! but in my case, the `finish()` must be out of `Handler().post {..}` scope :( – CodingBruceLee Aug 18 '22 at 03:00
  • in that case, you can set up a state variable, something like var canFinish = true in activity's onCreate(). Then set canFinish = false right before you call the Handler().post. And set canFinish = true right after toast.show(). Then, wherever you end up calling finish(), put a loop right before it: while(!canFinish){ sleep(100) }. That way, it'll wait until the toast is shown before it finishes the activity – user496854 Aug 18 '22 at 03:10
  • thank you! It's a good workaround :). but i want to know why the two layered handlers work well. – CodingBruceLee Aug 18 '22 at 03:22
  • I'm pretty sure that you're actually leaking the Activity context, so the inner handler doesn't get garbage-collected, and still runs after the activity is finished – user496854 Aug 18 '22 at 03:46
  • I checked leaking with Android profiler and Leak-canary many times. there were no any leaking. and also it doesn't matter even if i use `applicationContext`. – CodingBruceLee Aug 18 '22 at 04:17
  • Oh, you're right. after checking more times, there was context memory leak. But i don't still get it why the case wrapped handler works..if i use `ActivityContext` or `ApplicationContext`, then doesn't it happen in the case of single handler too? – CodingBruceLee Aug 18 '22 at 04:40
  • No, it doesn't matter which context you use. The 1st handler is properly registered, and then garbage-collected, so it dies when the activity is finished (and so, no Toast). But the nested one doesn't, so it's basically running without being tied to the activity, and that's why it doesn't get killed (and therefore actually generates the Toast) – user496854 Aug 18 '22 at 04:55
  • Thank you, i got it a little bit..but i have one more question. you said this is caused by nested handler structure. it sounds like the inner handler has an implicit reference to outer class. is it right? – CodingBruceLee Aug 18 '22 at 06:48
  • I'm sorry, but I don't really know the exact syntax of what's happening. I just know the overall process of how the activity lifecycle works, and what can cause problems – user496854 Aug 18 '22 at 07:09
  • You don't need to be sorry. it was really helpful to me. Thank you so much! – CodingBruceLee Aug 18 '22 at 07:22