I think AsyncTask is designed to provide a way to run things off the main thread and take care of starting and ending the thread as needed, but it's not intended to be re-used. It does it's thing in the background and then it's done.
As for the test case, what you say makes sense. And I suppose that it's just clarifying what I really already know about this. That it's blocking the main thread when executing the test. The AndroidTestCase class runs tests on the main thread. ActivityInstrumentationTestCase2 does not, rather using activity.runOnUIThread to execute tasks that must be done on the UI thread. However, the latter class only allows testing Activities, not generic classes that happen to use Handlers. So... it seems that by design, Android doesn't expect to have Handlers used in any classes except Activities. Is that right? Clearly Handlers are designed for two different purposes (scheduling to do something at a time and doing something on a different thread). Seems that they should be able to work independently of the Activity. Now, I'm confused, though. Clearly running a periodic, asynchronous task is a common pattern for mobile apps. If it were completely decoupled from the Activity, I'd use a Service. But in this case I want to start updates when the Activity is active and stop them when the Activity ends (not onPause, but onDestroy, although there are generic use cases for both types). What's the recommended approach here, still use a Service and pause it's activity when there are no registered Listeners? Create an AsyncTask that does the work and just launch it regularly from the Handler in the Activity? -- Jeremy Wadsack On Oct 8, 6:55 pm, DanH <[email protected]> wrote: > Re starting a thread, on most JVMs it's a relatively expensive > process, and it would be better to keep a thread "warm", with a > Handler waiting, than to start new ones for every operation. > > But of course that depends on how frequently you need the thread. > (Would be kinda nice if Android had an implicit async thread you could > throw stuff to, and Android started and ended the thread based on > usage and memory pressure, just like all the other components they > manage.) > > Re your test case, it doesn't work because you misunderstand how the > event loop works. Sending a Message to a Handler just queues the > message, and it doesn't get handled until the thread returns from > whatever work it's doing to the event loop. > > Presumably something in the event loop triggers a call to > testHandlerShouldReceiveMessagesPostedToIt. While that method is > running the event loop doesn't. While you're waiting in sleep, > nothing's happening -- sleep does not run the event loop. The event > loop will not run (and process your message) until you return from > testHandlerShouldReceiveMessagesPostedToIt. > > What this means is that any sort of sequential programming where you > "wait" for something must be broken into separate segments and > threaded together via Messages and Intents and whatnot -- waiting in > any form is verboten (at least in the UI thread, or any thread that > uses an event loop). Takes a whole new mindset to design an activity > that must, eg, handle a communication protocol or handshake with an > asynchronous activity. > > On Oct 8, 6:47 pm, Jeremy Wadsack <[email protected]> wrote: > > > > > Actually, having thought about this more, I recall my original intent > > was to only spawn the thread for the time needed to do the task. That > > means I don't have to worry about shutting down or closing the thread > > when the application closes. (The task responds to the application > > through one or more listeners that are Runnables, but it checks if > > they are null before calling them.) > > > This approach assumes that the gc in Dalvik won't mind spinning up > > objects and threads and destroying them regularly. I don't know if > > that's better practice for the jvm, but this seems less likely to > > produce leaks, right? > > > I changed the code to use a handler on the main thread. The handler, > > when it receives a message, starts a new thread with the long-running > > task. Unfortunately the handleMessage method is never called when the > > object is built from my test code. > > > So let's take the threading out of it for a moment. Why does this > > basic test case fail? > > > import android.os.Handler; > > import android.os.Message; > > > public class TaskManagerExample { > > > // Test field for inspection > > public boolean messageHandled = false; > > > private Handler handler = new Handler() { > > @Override > > public void handleMessage(Message msg) { > > messageHandled = true; > > } > > }; > > > public void start() { > > handler.sendEmptyMessage(0); > > } > > > } > > > import android.app.Application; > > import android.test.ApplicationTestCase; > > > public class TaskManagerExampleTest extends > > ApplicationTestCase<Application> { > > > public TaskManagerExampleTest() { > > super(Application.class); > > } > > > public void testHandlerShouldReceiveMessagesPostedToIt() { > > TaskManagerExample example = new TaskManagerExample(); > > example.start(); > > try { > > Thread.sleep(500); > > } catch (InterruptedException e) { > > } > > assertTrue(example.messageHandled); > > } > > > } > > > I put the sleep in there, just in case messages sent to the queue take > > some time to get processed. Seems half a second should be plenty of > > time (and keeps the test responsive). I tried it with five seconds and > > it still fails. > > > What base *TestCase class should I be using the get Handlers, Loopers > > and the MessageQueue to run under test? > > > -- > > Jeremy Wadsack > > > On Oct 8, 8:41 am, Lance Nanek <[email protected]> wrote: > > > > Handler has multiple constructors. The ones that take a Looper > > > argument can take the Looper for a Thread other than the current one. > > > That way you don't need to create the Handler on that Thread and it is > > > easier to ensure it is not null on the Thread using it. There's also > > > an android.os.HandlerThread utility class that is pretty much just a > > > Thread with a Looper. It has a getLooper method that's easy to feed > > > into the Handler constructor and that avoids the issue with prepare() > > > taking a while sometimes and leaving you with a null longer than you'd > > > expect. > > > > On Oct 7, 8:04 pm, DanH <[email protected]> wrote: > > > > > Come to think of it, how would one create a Handler for another > > > > thread? You'd have to dispatch the thread and have it create the > > > > Handler and pass the pointer back to you (we won't worry about how), > > > > but then that thread needs to go into a message receive loop. > > > > > Apparently this is done via Looper. I'm guessing the code would be > > > > like this: > > > > > class LooperThread extends Thread { > > > > public Handler mHandler; > > > > > public void run() { > > > > Looper.prepare(); > > > > > mHandler = new Handler(); > > > > > Looper.loop(); > > > > } > > > > } > > > > > You'd start this thread and then use your pointer to it to access > > > > mHandler (though you'd have to guard somehow against getting a null > > > > before the reference was set). Then dispatch Runnables or Messages > > > > via that handler. > > > > > (Gotta wonder where Looper stashes the instance it creates of itself > > > > to attach to the Thread. I suppose it uses ThreadLocal.) > > > > > On Oct 7, 4:38 pm, DanH <[email protected]> wrote: > > > > > > But I don't see anywhere in the Handler spec where it says the thread > > > > > will be dispatched. It appears to me that when the thread is posted, > > > > > Handler will just run its "run" method, without starting the thread. > > > > > If you wanted to run on a separate thread it appears to me that you'd > > > > > have to start the thread, have that thread create a Handler and pass > > > > > it back to you somehow, and then post via THAT Handler. > > > > > > At least that's how I'd interpret this: "Each Handler instance is > > > > > associated with a single thread and that thread's message queue. When > > > > > you create a new Handler, it is bound to the thread / message queue of > > > > > the thread that is creating it -- from that point on, it will deliver > > > > > messages and runnables to that message queue and execute them as they > > > > > come out of the message queue." > > > > > > On Oct 7, 4:25 pm, Jeremy Wadsack <[email protected]> wrote: > > > > > > > Fair point. The "// Do some tasks" is doing long-running (Internet- > > > > > > connected) stuff, so I want it to run in a separate thread. As I > > > > > > understand, posting the Runnable without a thread would run it on > > > > > > the > > > > > > main thread. > > > > > > > I assume the gc will clean up the Threads as they expire, but I > > > > > > could > > > > > > also redesign this to have a single active thread that posts > > > > > > Runnables > > > > > > (or even just messages) to it's own MessageQueue at specified > > > > > > intervals. Then I'd be managing the looper myself (that is, calling > > > > > > Looper.loop), which would probably resolve this issue. > > > > > > > That doesn't answer the original question but it may be the right > > > > > > approach if it's more "android-y". > > > > > > > -- > > > > > > Jeremy Wadsack > > > > > > > On Oct 7, 1:20 pm, DanH <[email protected]> wrote: > > > > > > > > Kind of off-topic, but why are you creating a new Thread with each > > > > > > > post, vs simply posting the Runnable? > > > > > > > > On Oct 5, 5:47 pm, Jeremy Wadsack <[email protected]> > > > > > > > wrote: > > > > > > > > > I have a class that uses a Handler for a timed, asynchronous > > > > > > > > activity. > > > > > > > > Something like this: > > > > > > > > > public class SampleClass { > > > > > > > > private static final long DELAY = 30000; > > > > > > > > private boolean isRunning = false; > > > > > > > > private Handler handler = new Handler(); > > > > > > > > > public start() { > > > > > > > > if (!isRunning) { > > > > > > > > isRunning = true; > > > > > > > > handler.post(new Thread(task)); > > > > > > > > } > > > > > > > > } > > > > > > > > > public stop() { > > > > > > > > isRunning = false; > > > > > > > > } > > > > > > > > > private Runnable task = new Runnable() { > > > > > > > > public void run() { > > > > > > > > if (!isRunning) { > > > > > > > > return; > > > > > > > > } > > > > > > > > > // Do some tasks > > > > > > > > handler.postDelayed(new Thread(this), DELAY); > > > > > > > > } > > > > > > > > } > > > > > > > > > } > > > > > > > > > I am trying to write a unit test (without having to implement an > > > > > > > > activity that instantiates the class) but I can't seem to get > > > > > > > > the > > > > > > > > items that are posted to the MessageQueue to ever be fired. > > > > > > > > Inheriting > > > > > > > > from junit.framework.TestCase doesn't work, but then there > > > > > > > > wouldn't be > > > > > > > > a MessageQueue for the handler, I'd expect (although, there's no > > > > > > > > error, but the Runnable never gets called). I tried inheriting > > > > > > > > the > > > > > > > > test class from AndroidTestCase and > > > > > > > > ApplicationTestCase<Application> > > > > > > > > but neither of those works, even > > ... > > read more » -- You received this message because you are subscribed to the Google Groups "Android Developers" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/android-developers?hl=en

