15

I'm trying to perform an action periodically. I want to create a new instance of a class after, say, ever 3 seconds. Would it be best to implement this by using a Handler or a Thread? Is there an easier, sort of dopey way I could try? I'm really not good at using threads - I want to learn, but it is more important that I get this to function before worrying about good programming practices.

    new Thread(){
        public void run(){
            //makes sure the player still has 3 lives left
            while(game == false){
                uiCallback.sendEmptyMessage(0);
                try {
                    Thread.sleep(2000); // wait two seconds before drawing the next flower
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } //sleep for 2 seconds
            }
        }
    }.start();
bohdan_trotsenko
  • 5,167
  • 3
  • 43
  • 70
Hani Honey
  • 2,101
  • 11
  • 48
  • 76

2 Answers2

20

I'm doing something similar in my android app; I update some data in my interface every 10 seconds. There are many ways to do this, but I chose to use a Handler because it's very simple to implement:

Thread timer = new Thread() {
    public void run () {
        for (;;) {
            // do stuff in a separate thread
            uiCallback.sendEmptyMessage(0);
            Thread.sleep(3000);    // sleep for 3 seconds
        }
    }
});
timer.start();
...
private Handler uiCallback = new Handler () {
    public void handleMessage (Message msg) {
        // do stuff with UI
    }
};

As you may know, you cannot run periodic functions like this in the UI thread, because it will block the UI. This creates a new Thread that sends a message to the UI when it is done, so you can update your UI with the new results of whatever your periodic function does.

If you do not need to update the UI with the results of this periodic function, you can simply ignore the second half of my code example, and just spawn a new Thread as shown. Beware, however: if you are modifying variables shared by this new Thread and the UI, you are going to run into problems if you don't synchronize. In general, threading is not an area where you want to ignore "good programming practices" because you'll get strange, non-predictable errors and you'll be cursing your program.

-tjw

Travis Webb
  • 14,688
  • 7
  • 55
  • 109
  • Hmmm, I'm just going to have to sit down one day and try to work it out. What I'm doing is implementing an infinite amount of images falling from the top of the screen, but I don't want them to fall all at once, I want it to be one say, every 5 seconds. This is all being performed in a SurfaceView, so hopefully I can still utilize this Handler. – Hani Honey Mar 27 '11 at 21:20
  • Yes, then my method is exactly what you want. The top code block will be your "timer", and the bottom code block will execute the code that will make a single image fall from the top of the screen. – Travis Webb Mar 27 '11 at 21:25
  • What are the arguments of the for loop meant to be? – Hani Honey Mar 27 '11 at 21:39
  • 3
    `for(;;)` means the loop runs forever. It's equivalent to `while(true)`. If you want it to stop at some point, you'll need to add in your own condition. – Travis Webb Mar 27 '11 at 22:31
  • Okay got it. So technically if I wanted this to run only while the state of the game was true (like while the person has 3 lives left or however I'm going to do it) I could replace that for statement with a while? while(game == true) ? – Hani Honey Mar 28 '11 at 16:55
  • Yes, `while(game) { ... }` would check and make sure the `game` variable is true first; when you exit the game, you'll want to call `timer.interrupt()` so that the loop exits immediately instead of waiting 3 seconds. – Travis Webb Mar 28 '11 at 17:40
  • somehow that isn't working. I was trying to put it in my surfaceDestroyed method, but first of all I don't have anything declared called timer. Am I just calling interrupt on the wrong thing? – Hani Honey Apr 05 '11 at 01:08
  • you don't have anything declared called timer? Look at my code... I basically copy/pasted it from my own working android app, with slight modification. `timer` should be global, probably. – Travis Webb Apr 05 '11 at 01:13
  • OH wow. I totally missed that >.> Wow. How did I miss that?? I was just writing it as new Thread(){"code follows" . However, when I do that, I get an error saying "cannot convert from void to Thread" – Hani Honey Apr 05 '11 at 01:18
  • `Thread timer = new Thread () { ... }` gives you a void conversion error? You need to inspect your code more closely; it's either another copy/paste error, or you have some other issue. – Travis Webb Apr 05 '11 at 01:20
  • I have something that looks like this. 'code'( new Thread(){ public void run(){ //makes sure the player still has 3 lives left while(game == false){ uiCallback.sendEmptyMessage(0); try { Thread.sleep(2000); // wait two seconds before drawing the next flower } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //sleep for 2 seconds } } }.start();) – Hani Honey Apr 05 '11 at 01:20
  • Ok, because the return type of `start()` is void. You need to call `start` after you instantiate the `Thread` object. It's a copy/paste error. You need to look at my answer yet again for the correct code... – Travis Webb Apr 05 '11 at 01:22
  • Really sorry, I can't work out how to use the mini-markdown formatting >.> – Hani Honey Apr 05 '11 at 01:22
  • Exactly. That code is wrong because you are calling `start()` in the wrong way. Look at mine more closely before you continue posting over and over. – Travis Webb Apr 05 '11 at 01:24
  • Thanks for the help, sorry I was such a bother. I think I just panicked. – Hani Honey Apr 05 '11 at 01:31
9

The simplest thing is to use postDelayed() on a View (e.g., a widget) to schedule a Runnable that does work, then reschedules itself.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491