I have an application that communicates with a Service in a remote process 
using the Messengerinterface. Here is the basic architecture of how things 
are set up:

   - The application generates several "Operation" objects that require 
   access to the service.
   - Each "Operation" contains a Handler wrapped in a Messenger used as a 
   callback receive the response data back from the Service
   - When the operation executes, it wraps its Messenger into an Intent and 
   calls startService() to pass the message to the remote service
   - The remote service does some work based on the parameters of the Intent 
and 
   then returns the response by sending a Message to the Messenger for that 
   operation.

Here is the basic code present in the operation:

public class SessionOperation {

    /* ... */

    public void runOperation() {
        Intent serviceIntent = new Intent(SERVICE_ACTION);
        /* Add some other extras specific to each operation */
        serviceIntent.putExtra(Intent.EXTRA_EMAIL, replyMessenger);

        context.startService(serviceIntent);
    }

    private Handler mAckHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //Process the service's response
        }
    };
    protected Messenger replyMessenger = new Messenger(mAckHandler);
}

And a basic snippet of how the service is structured:

public class WorkService extends Service {
    private ServiceHandler mServiceHandler;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //If intent has a message, queue it up
        Message msg = mServiceHandler.obtainMessage();
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);

        return START_STICKY;
    }

    private void onHandleIntent(Intent intent) {
        Messenger replyTarget = intent.getParcelableExtra(Intent.EXTRA_EMAIL);

        /* Do some work */

        Message delivery = Message.obtain(...);
        replyTarget.send(delivery);
    }
}

This all works fantastically well. I can send tons of operations from 
several different applications to the same service and they all process and 
send their response to just the right place. However...

I noticed that if the application ran long enough and with enough activity 
it would crash with anOutOfMemoryError. Upon looking at the HPROF data in 
MAT, I noticed that all these operations where staying in memory, and they 
were held hostage from the Garbage Collector because of theMessenger. 
Apparently, the Messenger instance is creating a long-term native 
connection to Binder that counts as a GC Root, which is keeping each 
"Operation" object in memory indefinitely.

[image: MAT Trace Example]

Does anyone know if there is a way to clear or disable the Messenger when 
the "Operation" is over so it doesn't create this memory leak?

Is there perhaps a better way to implement the IPC to the Servicein the 
same fashion, so that multiple disparate objects can make a request and get 
a result asynchronously?  I'm not convinced that just switching to using a 
bound service implementation will solve this issue as I would will need the 
Messenger to process the callback.

-- 
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

Reply via email to