Search code examples
androidkotlinhandlerrunnable

Problem with Handler(getMainLooper()).removeCallbacks in Kotlin, Android St


I’m fairly new to Kotlin and am working with Handlers and Runnables. I’ve written a very simple program with Start and Stop buttons as well as a TextView. When the Start button is pressed, this starts incrementing an Int variable i which is written to the TextView. This just keeps going and the aim is for the Stop button to stop the process. Using handler = Handler() it all works fine. However, I realise this is depreciated and so I’ve tried it with Handler(Looper.getMainLooper()) but the Stop button doesn’t work. I’m sure there’s a simple solution, but I can’t figure it out. Any help greatly appreciated. Here are the buttons and textview in the activity_main.xml

<TextView
    android:id="@+id/tv_number"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Number: "
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<Button
    android:id="@+id/btn_start"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="140dp"
    android:text="Start"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<Button
    android:id="@+id/btn_stop"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="50dp"
    android:text="Stop"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/btn_start" />

Here is the code in MainActivity.kt that works

class MainActivity : AppCompatActivity() {

private lateinit var runnable: Runnable
private lateinit var handler: Handler

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    var btnStart = findViewById<Button>(R.id.btn_start)
    var btnStop = findViewById<Button>(R.id.btn_stop)
    var tvNumber = findViewById<TextView>(R.id.tv_number)

    var i: Int

    handler = Handler()

    btnStart.setOnClickListener() {
        i = 0
        runnable = Runnable {
            i += 1
            tvNumber.text = "Number: $i"
            handler.postDelayed(
                runnable,
                500
            )
        }
        handler.postDelayed(
            runnable,
            1
        )
    }

    btnStop.setOnClickListener() {
        handler.removeCallbacks(runnable)
    }
}

}

Here is the code (just the setOnClickListener functions - the rest is the same) using Handler(Looper.getMainLooper()) where the Stop button doesn't work:

    btnStart.setOnClickListener() {
        i = 0
        runnable = Runnable {
            i += 1
            tvNumber.text = "Number: $i"
            Handler(Looper.getMainLooper()).postDelayed(
                runnable,
                500
            )
        }
        Handler(Looper.getMainLooper()).postDelayed(
            runnable,
            1
        )
    }

    btnStop.setOnClickListener() {
        Handler(Looper.getMainLooper()).removeCallbacks(runnable)
    }
}

}


Solution

  • your not-working code is working on new Handler every time. Passing same looper in constructor doesn't mean that this object is also the same. So you are calling removeCallbacks on freshly created Handler, which doesn't have any callbacks posted, thus not working (not removing from previously created Handler with posted runnable)

    btw. afaik creating Handler without a single argument also creates Handler with main Looper under the hood. but in first case you are referencing to one created handler object, so you can add callbacks or remove already posted