Reputation: 2304
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
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
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
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
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