10

I'm beginner in kotlin. I try to create a task that will repeat every 2 seconds. So I created something like this.

val handler = Handler()
    handler.postDelayed(Runnable {
        // TODO - Here is my logic

        // Repeat again after 2 seconds
        handler.postDelayed(this, 2000)
    }, 2000)

But in postDelayed(this) it gives error - required Runnable!, found MainActivity. I've tried even this@Runnable but it didn't work.

But when I write the same function like this, it works

val handler = Handler()
    handler.postDelayed(object : Runnable {
        override fun run() {
            // TODO - Here is my logic

            // Repeat again after 2 seconds
            handler.postDelayed(this, 2000)
        }
    }, 2000)

So why the this keyword doesn't work in first function, but in second function it works good?

Michael
  • 41,989
  • 11
  • 82
  • 128
Hayk Mkrtchyan
  • 2,835
  • 3
  • 19
  • 61

4 Answers4

5

You have several options to go about here:

  1. make both the runnable and the handler be in the same scope

        //class scope
        val handler = Handler()
        val runnable = object : Runnable {
           override fun run () {
             handler.removeCallbacksAndMessages(null) 
             //make sure you cancel the 
              previous task in case you scheduled one that has not run yet
             //do your thing
    
             handler.postDelayed(runnable,time)
          }
       }
    

then in some function

handler.postDelayed(runnable,time)
  1. You can run a timertask, which would be better in this case

     val task = TimerTask {
        override fun run() {
         //do your thing
        }
     }
    
     val timer = Timer()
    
     timer.scheduleAtFixedRate(task,0L, timeBetweenTasks)
    
Lena Bru
  • 13,521
  • 11
  • 61
  • 126
3

The first one is a function that accepts a lambda and returns a Runnable. In this case this means nothing.

The second one you're defining an anonymous object that implements Runnable. In this case this refers to that object instance.

m0skit0
  • 25,268
  • 11
  • 79
  • 127
  • Thank you very much :) – Hayk Mkrtchyan Mar 19 '20 at 17:34
  • @m0skit0 When you said 'The first one', did you mean `Runnable{...}` in the OP's first code example? If that's the case, could I ask you if there's any reference for this usage? Well, yeah in Kotlin we can omit a function's parenthesis if the argument is lambda, but I thought that's not a function, rather it's just a lambda, and the notation `Runnable` is just nothing but for avoiding confusion(I read it from Kotlin docs). Please let me know if I understood incorrectly. Thanks in advance. – starriet Dec 30 '20 at 01:14
  • @starriet `Runnable` is a [SAM conversion to lambda from a functional interface](https://kotlinlang.org/docs/reference/fun-interfaces.html). It is equivalent to the second example, it's just syntactic sugar to avoid verbosity. – m0skit0 Dec 30 '20 at 09:47
2

The below example will work.

val runnable = object : Runnable { 
        override fun run() {
         
            handler.postDelayed(this,1000)
        }
    }
Jatinder Kumar
  • 455
  • 5
  • 13
  • Yeap, when we create runnable in this way, we can get a reference to it by this keyword. But using lambda, it's not possible)) – Hayk Mkrtchyan Oct 07 '21 at 07:45
1

In your case , when use this it means "local final class <no name provided> : Runnable" , refer to a header runnable.

runnable=object : Runnable {
            override fun run() {
                // i is a counter
                println("No. "+i++)
                // Repeat again after 2 seconds
                handler.postDelayed(this, 2000)
            }
        }
        handler.postDelayed(runnable,0)

Where runnable is used inside of a method. As Handler() is deprecated, we must use like this:

var handler: Handler = Handler(Looper.getMainLooper())
var runnable: Runnable = Runnable { }

Moreover, anywhere you can stop this method by:

handler.removeCallbacks(runnable)
Mori
  • 2,653
  • 18
  • 24