1

The following message Handler works fine receiving messages from my service...

private  Handler handler = new Handler() 
{

    public void handleMessage(Message message) 
    {
        Object path = message.obj;

        if (message.arg1 == 5 && path != null)  //5 means its a single mapleg to plot on the map
        {
            String myString = (String) message.obj;
            Gson gson = new Gson();
            MapPlot mapleg = gson.fromJson(myString, MapPlot.class);
            myMapView.getOverlays().add(new DirectionPathOverlay(mapleg.fromPoint, mapleg.toPoint));
            mc.animateTo(mapleg.toPoint);

        }
        else
        {
            if (message.arg1 == RESULT_OK && path != null) 
            {
                Toast.makeText(PSActivity.this, "Service Started" + path.toString(), Toast.LENGTH_LONG).show();
            } 
            else 
            {
                Toast.makeText(PSActivity.this,"Service error" + String.valueOf(message.arg1),  Toast.LENGTH_LONG).show();          


            }

        }
    };
};

However, even though it tests out alright in the AVD (I'm feeding it a large KML file via DDMS) the "object path = message.obj;" line has a WARNING saying "this Handler class should be static else leaks might occur".

But if I say "static Handler handler = new Handler()" it won't compile complaining that I "cannot make a static reference to a non-static field myMapView. If I can't make such references, I can't do anything useful.

This led me into several hours of googling around on this issue and learning more about weakReferences than I ever wanted to know. The often found reccomendation I find is that I should replace...

private Handler handler = new Handler()

with

   static class handler extends Handler
{
    private final WeakReference<PSActivity> mTarget;
    handler(PSActivity target)
    {
        mTarget = new WeakReference<PSActivity>(target);
    }

But this won't compile still complaining that I can't make a static reference to a non-dtatic field. So, my question a week or to ago was "how can I write a message handler for android so my service can send data to my activity. Even though I have working code, the question still stands with the suffix "without leaking memory". Thanks, Gary

Dean Blakely
  • 3,535
  • 11
  • 51
  • 83

2 Answers2

5

I got the same warning message when I tried to use handler in a Service, and finally resolved it by taking the advice from this thread, see the code snippet from my project.

public class MyService extends Service {
    ...
    private MyHandler mHandler;

    public static class MyHandler extends Handler {
        private final WeakReference<MyService> mService;

        MyHandler(MyService service) {
            mService = new WeakReference<MyService>(service);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MyService service = mService.get();
            if (service!=null) {
                if (msg.what==MSG_RESUME_CHECKING) {
                    service.pause();
                } else if (msg.what==MSG_PAUSE_CHECKING) {
                    service.resume();
                }
            }
        }
    }
    ...

    @Override
    public void onCreate() {
        super.onCreate();
        ...
        mHandler = new MyHandler(this);
        ...
    }
}
Community
  • 1
  • 1
Ziteng Chen
  • 1,959
  • 13
  • 13
  • It doesn't look like you are making reference to any non-static objects so you would be okay. I am making such a reference. For now I have made myMapview and controller static but I may not always be able to do that. Try making such a reference and see what you get. – Dean Blakely Oct 17 '12 at 21:36
  • As I see the message.obj is a String, if it is not too long then you can ignore the warning (the length of a path is usually within 256 so it consumes at most a half kilo-byte, assume that you have 100 messages in queue then the handler holds 50kB, neglectable). If you have a good reason to worry about the potential memory leak then when you get the message you can assign a weak reference to the obj in the first place "msg.obj = new WeakReference(path);", and in the handleMessage check before use it "WeakReference ref = message.obj; String str = ref.get(); if (str!=null) ..." – Ziteng Chen Oct 18 '12 at 05:35
0

I know I'm a little late to the party, but hopefully this helps further answer the question for future inquirers.

As you discovered through your Googling (something I've done quite a bit of myself to solve a similar issue) you need to turn your Handler instance into a static inner class (nested class) which takes the target Activity in its constructor. It then converts this Activity reference into a WeakReference and that is what can be used to interact with things in your target Activity. In your case:

Toast.makeText(mTarget.get().this, "Service Started" + path.toString(), Toast.LENGTH_LONG).show();

Since you're changing to a nested class you'll also need to create an instance of that class for your Thread to access in its run() method. For more help on this (as well as on how to make sure your app works even after configuration changes) see this question.

Hope this helps!

Community
  • 1
  • 1
George
  • 153
  • 2
  • 8