9

I know the general problem of "Can't create handler inside thread that has not called Looper.prepare()" has been asked before, but I am struggling to understand how it applies in this case.

I am trying to construct a new CountDownTimer in a non-UI thread, which I guess is the cause of this error, but I don't really understand why the timer would need to be used in the main thread. From what I can see, it looks like it has a callback handler that needs to run in a thread that has a looper, which the non-UI thread does not have by default. It seems my options are: 1) Make this non-UI thread have a Looper or 2) make some strange method on my UI thread that can construct this timer, both which seem goofy to me. Can someone help me understand the implications?

Also, does anyone know of any useful links that shed light on the Looper and MessageQueue? I don't grasp them well, as I am sure I have shown. Thank you!

Jeff Axelrod
  • 27,676
  • 31
  • 147
  • 246
skaz
  • 21,962
  • 20
  • 69
  • 98

2 Answers2

6

An instance of CountDownTimer must be created on the UI thread.

If you had the custom class object:

public class MyTimer extends CountDownTimer{
    public MyTimer(...){
         super(duration,interval);
    }
    //... other code ...//
}

The construction of the object must be run on the UI thread

MyTimer mTimer = new MyTimer(...);   //can throw RuntimeException
                                    // with Looper.prepare() issue if
                                    // caller isn't UI thread

If multiple threads are creating and destroying the timer, make sure it's created on the UI thread by doing something like this:

MyActivity.runOnUiThread( new Runnable(){
     public void run(){
          mTimer = new MyTimer(...);
     }
});

but notice how the above code segment needs a reference to your Activity and to a class member variable mTimer

kpninja12
  • 529
  • 6
  • 11
  • 1
    The CountdownTimer doesn't need to be created on the UI thread. The CountdownTimer creates a Handler, which requires a message loop which is provided by Looper. – Mark Lummus May 21 '19 at 19:09
  • 1
    Yep. Tested on Android API 23. If i create it on a background thread, I get the infamous `Looper.prepare` exception. My custom class updates a TextView with the time remaining, though. – PJ_Finnegan Jan 10 '20 at 16:10
1

The timer doesn't need to be in a UI thread. But my guess is you're updating the UI to display the countdown count in that thread. Yu can't do that.

Use an asynctask and update the UI in onProgressUpdate

Falmarri
  • 47,727
  • 41
  • 151
  • 191
  • I don't believe this is the case. I have commented out all code inside the anonymous class, so all it does it call super in the contructor, but I am still getting the same error. Is it because the CountDownTimer has a callback method for when it is finished, and this needs to be "fired" by the looper? This is the confusion I am hoping to clear up. We can even abstract the problem away from my particular problem - I am just trying to get how it all fits together. Thank you. – skaz Oct 25 '10 at 12:20
  • Oh, I think I misunderstod your question. Can I ask why you're using a `CountdownTimer` in a nonUI thread? I think the point of `CountdownTimer` is to be able to easily update the UI thread. It looks like `onTick()` and `onFinish()` are supposed to run on the UI thread. Why not just use a regular java timer? – Falmarri Oct 25 '10 at 17:35
  • 1
    I guess I can use a regular timer. Regardless, why would CountDownTimer bomb in the constructor if I don't do any UI update? – skaz Oct 26 '10 at 21:36
  • Because every time it calls onTick() it expects to be on the UI thread probably – Falmarri Oct 26 '10 at 21:37
  • 1
    Why would onTick() require the UI Thread? This is what I don't understand. It just doesn't make sense to me why this handler would need to access the UI thread. Do all async methods that require an interrupt work through the UI thread? Thank you for your help. – skaz Oct 29 '10 at 00:50
  • Because that's the point of android's CountDownTimer, so you can update the UI onTick(). Just like the point of an AsyncTask. They're helper classes to make threading easier. If you don't need to update the UI, then just use a regular thread. – Falmarri Oct 29 '10 at 02:08
  • I put the constructor in the onProgressUpdate of AsyncTask but get the same error. Can I ask where you saw that Android's CountDownTimer is for UI updates? In the documentation I see for the onTick meethod it just says you can make regular updates there - no mention of the UI thread. Thanks. – skaz Oct 31 '10 at 20:44
  • I am already in the context of a background thread, so I don't think AsyncTask will work for me either. I will try a timer. – skaz Oct 31 '10 at 21:01
  • How can I get ahold of the UI thread from this background thread? Will I need a reference to the Activity so I can call runOnUIThread or is there a better way? – skaz Oct 31 '10 at 21:03
  • I'm not saying use an asynctask. I'm saying that the point of a countdowntimer is similar to the use case of an asynctask. It's a wraper around a thread so you don't need to use a handler. If you don't need UI updates, just use a regular thread. – Falmarri Oct 31 '10 at 23:13
  • i think that , you can run it on the method : Activity.runOnUiThread(new Runnable); and then you can update your UI :) – Houcine May 26 '11 at 22:40