Fix a memory leak that was observed each time a recording session was stopped.

This commit is contained in:
Bartlomiej Niechwiej
2010-11-21 23:49:17 -08:00
parent ffb73569e9
commit 0e623ebfeb
@@ -710,7 +710,6 @@ public class TrackRecordingService extends Service implements LocationListener {
showNotification(); showNotification();
prefManager.shutdown(); prefManager.shutdown();
prefManager = null; prefManager = null;
binder = null;
checkLocationListener.cancel(); checkLocationListener.cancel();
checkLocationListener = null; checkLocationListener = null;
timer.cancel(); timer.cancel();
@@ -721,6 +720,15 @@ public class TrackRecordingService extends Service implements LocationListener {
signalManager = null; signalManager = null;
splitManager.shutdown(); splitManager.shutdown();
splitManager = null; splitManager = null;
// Make sure we have no indirect references to this service.
locationManager = null;
notificationManager = null;
providerUtils = null;
binder.detachFromService();
binder = null;
// This should be the last operation.
releaseWakeLock(); releaseWakeLock();
super.onDestroy(); super.onDestroy();
@@ -896,75 +904,98 @@ public class TrackRecordingService extends Service implements LocationListener {
updateCurrentWaypoint(); updateCurrentWaypoint();
return Long.parseLong(uri.getLastPathSegment()); return Long.parseLong(uri.getLastPathSegment());
} }
private ServiceBinder binder = new ServiceBinder(this);
/** /**
* The ITrackRecordingService is defined through IDL. * TODO: There is a bug in Android that leaks Binder instances. This bug is
* especially visible if we have a non-static class, as there is no way to
* nullify reference to the outer class (the service).
* A workaround is to use a static class and explicitly clear service
* and detach it from the underlying Binder. With this approach, we minimize
* the leak to 24 bytes per each service instance.
*
* For more details, see the following bug:
* http://code.google.com/p/android/issues/detail?id=6426.
*/ */
private ITrackRecordingService.Stub binder = private static class ServiceBinder extends ITrackRecordingService.Stub {
new ITrackRecordingService.Stub() { private TrackRecordingService service;
@Override
public boolean isRecording() { public ServiceBinder(TrackRecordingService service) {
return TrackRecordingService.this.isRecording(); this.service = service;
} }
/**
* Clears the reference to the outer class to minimize the leak.
*/
public void detachFromService() {
this.service = null;
attachInterface(null, null);
}
@Override
public boolean isRecording() {
return service.isRecording();
}
@Override @Override
public long getRecordingTrackId() { public long getRecordingTrackId() {
return recordingTrackId; return service.recordingTrackId;
} }
@Override @Override
public boolean hasRecorded() { public boolean hasRecorded() {
return providerUtils.getLastTrackId() >= 0; return service.providerUtils.getLastTrackId() >= 0;
} }
@Override @Override
public long startNewTrack() { public long startNewTrack() {
return TrackRecordingService.this.startNewTrack(); return service.startNewTrack();
} }
/** /**
* Insert the given waypoint marker. Users can insert waypoint markers * Insert the given waypoint marker. Users can insert waypoint markers
* to tag locations with a name, description, category etc. * to tag locations with a name, description, category etc.
* *
* @param waypoint a waypoint * @param waypoint a waypoint
* @return the unique id of the inserted marker * @return the unique id of the inserted marker
*/ */
@Override @Override
public long insertWaypointMarker(Waypoint waypoint) { public long insertWaypointMarker(Waypoint waypoint) {
return TrackRecordingService.this.insertWaypointMarker(waypoint); return service.insertWaypointMarker(waypoint);
} }
/** /**
* Insert a statistics marker. A statistics marker holds the stats for * Insert a statistics marker. A statistics marker holds the stats for
* the last segment up to this marker. * the last segment up to this marker.
* *
* @param location the location where to insert * @param location the location where to insert
* @return the unique id of the inserted marker * @return the unique id of the inserted marker
*/ */
@Override @Override
public long insertStatisticsMarker(Location location) { public long insertStatisticsMarker(Location location) {
return TrackRecordingService.this.insertStatisticsMarker(location); return service.insertStatisticsMarker(location);
} }
@Override @Override
public void endCurrentTrack() { public void endCurrentTrack() {
TrackRecordingService.this.endCurrentTrack(); service.endCurrentTrack();
} }
@Override @Override
public void deleteAllTracks() { public void deleteAllTracks() {
if (isRecording()) { if (isRecording()) {
throw new IllegalStateException( throw new IllegalStateException(
"Cannot delete all tracks while recording!"); "Cannot delete all tracks while recording!");
} }
providerUtils.deleteAllTracks(); service.providerUtils.deleteAllTracks();
} }
@Override @Override
public void recordLocation(Location loc) { public void recordLocation(Location loc) {
onLocationChanged(loc); service.onLocationChanged(loc);
} }
}; }
public long startNewTrack() { public long startNewTrack() {
Log.d(MyTracksConstants.TAG, "TrackRecordingService.startNewTrack"); Log.d(MyTracksConstants.TAG, "TrackRecordingService.startNewTrack");