387

I developed an application to display some text at defined intervals in the Android emulator screen. I am using the Handler class. Here is a snippet from my code:

handler = new Handler();
Runnable r = new Runnable() {
    public void run() {
        tv.append("Hello World");               
    }
};
handler.postDelayed(r, 1000);

When I run this application the text is displayed only once. Why?

Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
Rajapandian
  • 9,257
  • 24
  • 74
  • 86

11 Answers11

574

The simple fix to your example is :

handler = new Handler();

final Runnable r = new Runnable() {
    public void run() {
        tv.append("Hello World");
        handler.postDelayed(this, 1000);
    }
};

handler.postDelayed(r, 1000);

Or we can use normal thread for example (with original Runner) :

Thread thread = new Thread() {
    @Override
    public void run() {
        try {
            while(true) {
                sleep(1000);
                handler.post(this);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};

thread.start();

You may consider your runnable object just as a command that can be sent to the message queue for execution, and handler as just a helper object used to send that command.

More details are here http://developer.android.com/reference/android/os/Handler.html

zgc7009
  • 3,371
  • 5
  • 22
  • 34
alex2k8
  • 42,496
  • 57
  • 170
  • 221
  • Alex, i have one small doubt.Now the thread is running perfectly and displaying the text continously, if i want to stop this means what i have to do?Please help me. – Rajapandian Dec 17 '09 at 14:19
  • 11
    You may define boolean variable _stop, and set it 'true' when you want to stop. And change 'while(true)' into 'while(!_stop)', or if the first sample used, just change to 'if(!_stop) handler.postDelayed(this, 1000)'. – alex2k8 Dec 17 '09 at 22:01
  • 1
    If you want to be sure that Handler will be attached to the main thread, you should initialize it like this: handler = new Handler(Looper.getMainLooper()); – Yair Kukielka Jul 09 '15 at 00:20
  • @alex2k8 Shouldn't it be handler.post(r) instead of handler.post(this) in the second snippet? Moreover, if you want to use the solution with Thread, you also have to remove handler.postDelayed(this, 1000) from the Runnable r, as the delay is already executed inside of run() method of the Thread object. – Jerry Mar 14 '18 at 14:57
  • @alex2k8 - Hi bro! Please tell me how to stop the 2nd snippet `Thread` manually while running? – Irfan Akram Apr 08 '20 at 21:13
55
new Handler().postDelayed(new Runnable() {
    public void run() {
        // do something...              
    }
}, 100);
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
user2212515
  • 1,220
  • 1
  • 12
  • 10
  • 2
    If you want to be sure that Handler will be attached to the main thread, you should initialize it like this: new Handler(Looper.getMainLooper()); – Yair Kukielka Jul 09 '15 at 00:23
  • 1
    Isn't this solution equivalent to the original post? It would only run the Runnable once after 100 milliseconds. – tronman May 10 '18 at 15:38
40

I think can improve first solution of Alex2k8 for update correct each second

1.Original code:

public void run() {
    tv.append("Hello World");
    handler.postDelayed(this, 1000);
}

2.Analysis

  • In above cost, assume tv.append("Hello Word") cost T milliseconds, after display 500 times delayed time is 500*T milliseconds
  • It will increase delayed when run long time

3. Solution

To avoid that Just change order of postDelayed(), to avoid delayed:

public void run() {
    handler.postDelayed(this, 1000);
    tv.append("Hello World");
}
Paritosh
  • 2,097
  • 3
  • 30
  • 42
NguyenDat
  • 4,129
  • 3
  • 46
  • 46
  • 6
    -1 you are assuming the task you perform in the run() is a costs a constant amount each run, if this were operation on dynamic data (which typically it is) then you would end up with more than one run() occuring at once. This is why postDelayed typically is placed at the end. – Jay Feb 24 '12 at 00:18
  • 1
    @Jay Unfortunately you are wrong. A Handler is associated with a single Thread (and a Looper which is the run method of that Thread) + a MessageQueue. Each time you post a Message you enqueue it and the next time the looper checks the queue it executes the run method of the Runnable you posted. Since that is all happens in just one Thread you can't have more than 1 executed at the same time. Also doing the postDelayed first will get you closer to a 1000ms per execution because internally it uses current time + 1000 as execution time. If you put code before the post you add additional delay. – zapl Apr 27 '12 at 23:42
  • 1
    @zapl thanks for the tip about the handler, I assumed it would be executing multiple runnables and hence, multiple threads. Internally though, a condition such as if ((currenttime - lastruntime)>1000) will work fine when run durations are less than or equal to 1000ms, however, when this is exceeded, surely the timer is going to be occuring at non linear intervals dependant entirely on execution time of the run method (hence my point on unpredictable computational expense) – Jay May 03 '12 at 11:36
  • If you want a fixed period, w/out conflict, measure the start time before doing work, and adjust your delay accordingly. You will still see a little latency if the cpu is busy, but it can allow you a stricter period, and to detect if the system is overloaded (perhaps to signal low priority stuff to back off). – Ajax Jan 01 '13 at 16:29
31

For repeating task you can use

new Timer().scheduleAtFixedRate(task, runAfterADelayForFirstTime, repeaingTimeInterval);

call it like

new Timer().scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {

            }
        },500,1000);

The above code will run first time after half second(500) and repeat itself after each second(1000)

Where

task being the method to be executed

after the time to initial execution

(interval the time for repeating the execution)

Secondly

And you can also use CountDownTimer if you want to execute a Task number of times.

    new CountDownTimer(40000, 1000) { //40000 milli seconds is total time, 1000 milli seconds is time interval

     public void onTick(long millisUntilFinished) {
      }
      public void onFinish() {
     }
    }.start();

//Above codes run 40 times after each second

And you can also do it with runnable. create a runnable method like

Runnable runnable = new Runnable()
    {
        @Override
        public void run()
        {

        }
    };

And call it in both these ways

new Handler().postDelayed(runnable, 500 );//where 500 is delayMillis  // to work on mainThread

OR

new Thread(runnable).start();//to work in Background 
Zar E Ahmer
  • 33,936
  • 20
  • 234
  • 300
25

I believe for this typical case, i.e. to run something with a fixed interval, Timer is more appropriate. Here is a simple example:

myTimer = new Timer();
myTimer.schedule(new TimerTask() {          
@Override
public void run() {
    // If you want to modify a view in your Activity
    MyActivity.this.runOnUiThread(new Runnable()
        public void run(){
            tv.append("Hello World");
        });
    }
}, 1000, 1000); // initial delay 1 second, interval 1 second

Using Timer has few advantages:

  • Initial delay and the interval can be easily specified in the schedule function arguments
  • The timer can be stopped by simply calling myTimer.cancel()
  • If you want to have only one thread running, remember to call myTimer.cancel() before scheduling a new one (if myTimer is not null)
Paritosh
  • 2,097
  • 3
  • 30
  • 42
iTech
  • 18,192
  • 4
  • 57
  • 80
  • 7
    I don't beleive that a timer is more appropriate as it does not consider the android life cycle. When you pause and resume, there is no guarentee that the timer will run correctly. I would argue that a runnable is the better choice. – Janpan Jan 15 '14 at 06:27
  • 1
    Does that mean when an app is put in the background a Handler will be paused? and when it regains focus it will continue (more or less) as if nothing had happened? – Andrew G Jan 15 '15 at 19:53
17
Handler handler=new Handler();
Runnable r = new Runnable(){
    public void run() {
        tv.append("Hello World");                       
        handler.postDelayed(r, 1000);
    }
}; 
handler.post(r);
Dzhuneyt
  • 8,437
  • 14
  • 64
  • 118
singh arjun
  • 179
  • 1
  • 2
6

Kotlin

private lateinit var runnable: Runnable
override fun onCreate(savedInstanceState: Bundle?) {
    val handler = Handler()
    runnable = Runnable {
        // do your work
        handler.postDelayed(runnable, 2000)
    }
    handler.postDelayed(runnable, 2000)
}

Java

Runnable runnable;
Handler handler;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    handler = new Handler();
    runnable = new Runnable() {
        @Override
        public void run() {
            // do your work
            handler.postDelayed(this, 1000);
        }
    };
    handler.postDelayed(runnable, 1000);
}
Khemraj Sharma
  • 57,232
  • 27
  • 203
  • 212
4

If I understand correctly the documentation of Handler.post() method:

Causes the Runnable r to be added to the message queue. The runnable will be run on the thread to which this handler is attached.

So examples provided by @alex2k8, even though are working correctly, are not the same. In case, where Handler.post() is used, no new threads are created. You just post Runnable to the thread with Handler to be executed by EDT. After that, EDT only executes Runnable.run(), nothing else.

Remember: Runnable != Thread.

Damian Walczak
  • 1,334
  • 14
  • 21
  • 1
    True that. Don't create a new thread every time, ever. The whole point of Handler and other execution pools is to have one or two threads pull tasks off a queue, to avoid thread creation and GC. If you have a really leaky app, the extra GC might help cover up OutOfMemory situations, but the better solution in both cases is to avoid creating more work than you need to. – Ajax Jan 01 '13 at 16:32
  • So better way to do this is by using the normal thread based on alex2k8's answer? – Compaq LE2202x Sep 04 '13 at 07:57
2

Kotlin with Coroutines

In Kotlin, using coroutines you can do the following:

CoroutineScope(Dispatchers.Main).launch { // Main, because UI is changed
    ticker(delayMillis = 1000, initialDelayMillis = 1000).consumeEach {
        tv.append("Hello World")
    }
}

Try it out here!

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
1

An interesting example is you can continuously see a counter/stop-watch running in separate thread. Also showing GPS-Location. While main activity User Interface Thread is already there.

Excerpt:

try {    
    cnt++; scnt++;
    now=System.currentTimeMillis();
    r=rand.nextInt(6); r++;    
    loc=lm.getLastKnownLocation(best);    

    if(loc!=null) { 
        lat=loc.getLatitude();
        lng=loc.getLongitude(); 
    }    

    Thread.sleep(100); 
    handler.sendMessage(handler.obtainMessage());
} catch (InterruptedException e) {   
    Toast.makeText(this, "Error="+e.toString(), Toast.LENGTH_LONG).show();
}

To look at code see here:

Thread example displaying GPS Location and Current Time runnable alongside main-activity's User Interface Thread

mdrafiqulrabin
  • 1,657
  • 5
  • 28
  • 38
  • 1
    Hint: if you want to make your answer useful - learn how to format input here. That *preview* window exists for a reason. – GhostCat Aug 25 '17 at 14:11
0

now in Kotlin you can run threads this way:

class SimpleRunnable: Runnable {
    public override fun run() {
        println("${Thread.currentThread()} has run.")
    }
}
fun main(args: Array<String>) {
    val thread = SimpleThread()
    thread.start() // Will output: Thread[Thread-0,5,main] has run.
    val runnable = SimpleRunnable()
    val thread1 = Thread(runnable)
    thread1.start() // Will output: Thread[Thread-1,5,main] has run
}
André Abboud
  • 1,864
  • 2
  • 14
  • 23