5

I have an app with a two threads - main and data loader. When data loader finishes it posts a Runnable object to the main thread (as described in the DevGuide), but it never gets delivered and run.

Here's the basic code:

class MyApp extends Application
{
   public void onCreate()
   {
         LoaderThread t = new LoaderThread();
         t.start();
   }

   private class LoaderThread extends Thread
   {
        public void run()
        {
             SystemClock.sleep(2000);
             boolean res = m_handler.post(m_runnable);
             if(res)
                Log.d(TAG, "Posted Runnable");
        }
   }

   private final Handler m_handler = new Handler();
   private final Runnable m_runnable = new Runnable() {
             public void run()
             {
                 Log.d(TAG, "Hey, i'm runnable!");
             }
        }
}

Also it maybe important to note that I ran this code as a unit-test derived from an ApplicationTestCase:

class MyAppTest : public ApplicationTestCase
{
     public MyAppTest()
     {
          super(MyApp.class);
     }

     public void testLoading()
     {
          createApplication();
          // few asserts follow here...
     }
}

So this fails. Runnable never gets run() called, although the log indicates that it has been posted successfully. I also tried to send simple messages instead of posting runnable (m_handler.sendEmptyMessage(1) for example) - they never get delivered to handler's callback in the main thread.

What do I miss here?

Thanks in advance :)

dimsuz
  • 8,969
  • 8
  • 54
  • 88

3 Answers3

1

A Handler requires a Looper in order to work. The Looper provides a message queue required by the Handler.

All instances of Activity have a Looper as one is used to process UI Events, but you can create your instance of Looper elsewhere.

Have a look in your Log output to see if Android is complaining about the absence of a Looper.

If it is, you might be able to fix by add the following to the top of your onCreate() method:

Looper.prepare(); 
m_handler = new Handler();
Looper.run();

And remove the initialisation of m_handler from later in your code.

David Webb
  • 190,537
  • 57
  • 313
  • 299
  • I thought so to, but the Looper is created indeed. Because when I try to create it by myself (including trying your suggestion above) I get: "java.lang.RuntimeException: Only one Looper may be created per thread" Also I just found that the code (unchanged) works fine when ran inside a full blown app in OS. On the other hand, when I run it as a test case it fails to work... Any hints? Looper is ok, it is not null, no exceptions, no errors in log. I'm puzzled. – dimsuz Feb 18 '10 at 16:46
  • Thanks for this hint! My code was not within an Activity and therefore not within a Looper... I was trying to figure out why the messages sent over the handler are only delivered when the calling thread is stopped. – Sney Jun 22 '11 at 06:08
0

Handler only works in an Activity AFAIK. You are attempting to use it in an Application.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • I just found that this very code works when run as an application in OS, but fails when running as a test... So I guess this is something else missing. maybe related to activity... – dimsuz Feb 18 '10 at 16:40
0

An alternative to calling Looper.prepare() is to call new Handler(Looper.getMainLooper()). The problem with calling Looper.prepare() is that it will throw an exception when there is already a looper on your thread. Chances are you are writing code that has to run under different environments and this solution will handle more cases.

See: AsyncTask and Looper.prepare() error

Community
  • 1
  • 1
ThomasW
  • 16,981
  • 4
  • 79
  • 106