user2224350
user2224350

Reputation: 2304

Android service-binder leaks?

I recently ran into a strange memory problem. Everytime I start MyActivity (which launches MyService) the memory usage of my app increases. So after starting and finishing MyActivity a few time my app runs out of memory. I did a heap dump after starting and finishing MyActivity twice. I found that there a 2 instances of MyService in memory. After I did 'incoming references'-> 'path to gc roots' I got following line (on both instances):

com.....MyService @ 0x42746880

->this$0 com.....MyService$MyServiceBinder @42746928 Native Stack

My code is similiar to this:

public class MyService extends Service {

    ...

    public class MyServiceBinder extends Binder{
        MyService getService(){
            return MyService.this;
        }
    }

}

public class MyActivity extends Activity{

    private MyService service;

    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        launchService(savedInstanceState==null); //start MyService
    }

    ...

    @Override
    protected void onStart(){
        super.onStart();
        if(clientService == null)
            getApplicationContext().bindService(new Intent(this, MyService.class), connection, Context.BIND_NOT_FOREGROUND);
    }

    @Override
    protected void onStop(){
        super.onStop();
        if(clientService != null)
            getApplicationContext().unbindService(connection);
    }

    @Override
    protected void onDestroy(){
        super.onDestroy();
    }

    @Override
    protected void onBackPressed(){
        service.destroyService();  //stops MyService ->calls stopSelf inside MyService
        finish();
    }

private LocalServiceConnection connection = new LocalServiceConnection(this);

private static class LocalServiceConnection implements ServiceConnection{

        private final WeakReference<MyActivity> parent;

        private MyActivity activity;

        private MyServiceBinder binder;

public LocalServiceConnection(MyActivity parent){
    this.parent = new WeakReference<MyActivity>(parent);
        }

@Override
public void onServiceConnected(ComponentName arg0, IBinder arg1) {
            activity = parent.get();
            if(activity==null)
                return;
            binder = (MyServiceBinder) arg1;
            activity.service = binder.getService();
            ...
            activity = null;

        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            activity = parent.get();
            binder = null;
            if(activity==null)
                return;
            activity.service = null;
            activity = null;
        }

    }
}

I'm not totaly sure if this reference to MyService in MyServiceBinder is the source of the leak. So could there be a leak in my code?

thx & reagards

EDIT:

private void launchService(boolean isFirst){
    if(service == null){
        if(isFirst)
            startService(new Intent(this, MyService.class);
    }
}

If I "normally" close MyActivity the service is stopped and thus onServiceDisconnect gets called before onDestroy -> unbindService is not called

EDIT2:

I made some heap dumps again and forced GC via ddms (cause GC). I found that MyService, MyServiceBinder and LocalServiceConnection can't get garbage collected, because they were still remaining in memory while all other instances had been released. MyService and LocalServiceConnection have living references to the MyServiceBinder-object. MyServiceBinder seems to have no path to gc roots. The question is why this Binder-objects don't get garbage collected??? Could it be that MyServiceBinder references MyService which in turn references MyServiceBinder? Maybe - if such kind of loop does exist - therefore the MyServiceBinder-object can't get released??

Upvotes: 1

Views: 5096

Answers (4)

Jason Crosby
Jason Crosby

Reputation: 3573

I see that your service is marked as private, do you have any getter methods for the service to make it available to other classes? If you do, you should check where you use the getter method. If another class is keeping a reference to the service it wont get gc'd. Also a big problem in Android is passing references of an Activity around to other classes. If you do this, and the other class holds onto a reference to the Activity, it wont be gc'd. And since it looks like the service, binder and LocalServiceConnection are held within your Activity, none of them will be gc'd either if a reference to the Activity is not released.

Upvotes: 0

user2224350
user2224350

Reputation: 2304

So I made the Binder-class a standalone class with WeakReference to the service. That didn't fix the problem with the binder and serviceConnection-objects (they still don't get garbage collected), but the "huge" service-object gets collected..

Upvotes: 2

Srikanth Roopa
Srikanth Roopa

Reputation: 1790

Make Sure u unbind the service in onPause Method:

Bind the Service:

 @Override
  protected void onResume() {
    super.onResume();
    bindService();
  }

unBind the service:

 @Override
  protected void onPause() {
    super.onPause();
    unbindService();
  }

Upvotes: 2

Abdullah Shoaib
Abdullah Shoaib

Reputation: 2095

In your onCreate() check service for null this way and then call launchService()

if (service == null){
   launchService();
} 

and in your onDestroy() do the same; check if it's not null and then call destroyService():

if (service !=null){
    destroyService();
}

Upvotes: 0

Related Questions