2

I'm rather perplexed about this one. I'm taking this Coursera course, and working on Assignment 5. I was able to complete the assignment with minimal difficulties, and then proceeded to try and make the code do my own purpose. I made a few tweaks to it as well, but I'm having a significant problem, which I can't for the life of me figure out. The Handler doesn't seem to take messages at all... Any idea what I'm doing wrong?

Here's a few bits of my code. Let me know if there's something else you'd like to see.

Handler:

public class DownloadHandler extends Handler {
private WeakReference<Context> mContext;
public DownloadHandler(Context context) {
    super();
    mContext = new WeakReference<Context>(context);
    Log.v(TAG,"Created! "+this);
}
    // Handle any messages that get sent to this Handler
    @Override
    public void handleMessage(Message msg) {
        Log.v(TAG, "" + msg); //Never reached
                ...
       }
}

Before I go any further, I've tested the handler by sending a simple message (handler.sendEmptyMessage(0);, with no success. Any ideas?

Log.v(TAG,"handler="+handler);
Log.v(TAG,"Sending test "+handler.sendEmptyMessage(0));

And the logcat:

06-14 22:47:34.700: V/DownloadAddOnProducts(7406): handler=Handler (com.kd7uiy.hamfinder.DownloadHandler) {527724dc}
06-14 22:47:34.700: V/DownloadAddOnProducts(7406): Sending test true

To reduce this even further, I have a unit test:

public class DownloadTester extends InstrumentationTestCase {
    public void setUp() {
        targetContext=getInstrumentation().getTargetContext();
    }

    public void testDownloadHandler() {
        Looper.myLooper().setMessageLogging(new LogPrinter(Log.VERBOSE,"DownloadLooper"));
        DownloadHandler handler=new DownloadHandler(targetContext,Looper.myLooper());
        Log.v(TAG,"handler="+handler);
        handler.sendEmptyMessage(0);
        Log.v(TAG,"Send empty message");
    }

And the logcat shows:

06-14 23:03:46.068: V/DownloadHandler(8337): Created! Handler (com.kd7uiy.hamfinder.DownloadHandler) {52770be8}
06-14 23:03:46.068: V/DownloadHandler(8337): Created! Handler (com.kd7uiy.hamfinder.DownloadHandler) {52770f60}
06-14 23:03:46.068: V/DownloadTester(8337): handler=Handler (com.kd7uiy.hamfinder.DownloadHandler) {52770f60}
06-14 23:03:46.068: V/DownloadTester(8337): Send empty message

It created two DownloadHandlers because the other one is for another unit test in my set of unit tests.

PearsonArtPhoto
  • 38,970
  • 17
  • 111
  • 142

3 Answers3

4

Quoting the documentation for Handler:

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. 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.

Quoting the documentation for the zero-argument Handler constructor that you are using:

Default constructor associates this handler with the Looper for the current thread. If this thread does not have a looper, this handler won't be able to receive messages so an exception is thrown.

If you are not crashing, and you have not created your own HandlerThread (or the equivalent), then your DownloadHandler is presumably tying itself to the main application thread. Your messages that you send to that DownloadHandler will only be passed to the handleMessage() method when the main application thread gets around to it, after having processed all other messages on its message queue. And, so long as you are using the main application thread (e.g., executing onCreate() of a Service), that message will not appear (and your UI will be frozen to boot).

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

The problem is that I was using a Handler on a non-UI thread. This can be done, but it takes a bit of work. The trick to making this work is to make sure you call Looper.loop() on the thread in question, as this question makes reference too. So this Unit Test code works:

public void testDownloadHandler() {
    Looper.myLooper().setMessageLogging(new LogPrinter(Log.VERBOSE,"DownloadLooper"));
    DownloadHandler handler=new DownloadHandler(targetContext,Looper.myLooper());
    Log.v(TAG,"handler="+handler);
    handler.sendEmptyMessage(0);
    Log.v(TAG,"Send empty message");
    Log.v(TAG,"Looper="+handler.getLooper());
    Log.v(TAG,""+Looper.myQueue());
    Looper.loop();
}

In fact, in my case I want to do some fairly significant processing of the file after I receive it, so I don't want it to run in the UI thread. But I don't want to mess with the Looper.loop() and possible choke my helper thread (It's a part of a ThreadPoolExecutioner pool of threads, and I don't want it hung). So I've decided I'm going to use the Main Looper message pool to manage the Handler, and then have the Handler manage a ThreadPoolExecutioner set of pools to post-process the data after the Service is done with it.

Community
  • 1
  • 1
PearsonArtPhoto
  • 38,970
  • 17
  • 111
  • 142
1

Try testing your handler on the activity's onCreate().

Something like the following:

protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState);

    DownloadHandler dh = new DownloadHandler(getMainLooper());
    dh.sendEmptyMessage(0);
}
Ryhan
  • 1,815
  • 1
  • 18
  • 22
  • yeah thats right, you don't have to pass in the mainLooper() on the onCreate() at least since onCreate() is guaranteed to run on main ui thread – Ryhan Jun 14 '14 at 23:27