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();
@@ -897,74 +905,97 @@ public class TrackRecordingService extends Service implements LocationListener {
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() {
return TrackRecordingService.this.isRecording();
}
@Override public ServiceBinder(TrackRecordingService service) {
public long getRecordingTrackId() { this.service = service;
return recordingTrackId; }
}
@Override /**
public boolean hasRecorded() { * Clears the reference to the outer class to minimize the leak.
return providerUtils.getLastTrackId() >= 0; */
} public void detachFromService() {
this.service = null;
attachInterface(null, null);
}
@Override @Override
public long startNewTrack() { public boolean isRecording() {
return TrackRecordingService.this.startNewTrack(); return service.isRecording();
} }
/** @Override
* Insert the given waypoint marker. Users can insert waypoint markers public long getRecordingTrackId() {
* to tag locations with a name, description, category etc. return service.recordingTrackId;
* }
* @param waypoint a waypoint
* @return the unique id of the inserted marker
*/
@Override
public long insertWaypointMarker(Waypoint waypoint) {
return TrackRecordingService.this.insertWaypointMarker(waypoint);
}
/** @Override
* Insert a statistics marker. A statistics marker holds the stats for public boolean hasRecorded() {
* the last segment up to this marker. return service.providerUtils.getLastTrackId() >= 0;
* }
* @param location the location where to insert
* @return the unique id of the inserted marker
*/
@Override
public long insertStatisticsMarker(Location location) {
return TrackRecordingService.this.insertStatisticsMarker(location);
}
@Override @Override
public void endCurrentTrack() { public long startNewTrack() {
TrackRecordingService.this.endCurrentTrack(); return service.startNewTrack();
} }
@Override /**
public void deleteAllTracks() { * Insert the given waypoint marker. Users can insert waypoint markers
if (isRecording()) { * to tag locations with a name, description, category etc.
throw new IllegalStateException( *
"Cannot delete all tracks while recording!"); * @param waypoint a waypoint
} * @return the unique id of the inserted marker
providerUtils.deleteAllTracks(); */
} @Override
public long insertWaypointMarker(Waypoint waypoint) {
return service.insertWaypointMarker(waypoint);
}
@Override /**
public void recordLocation(Location loc) { * Insert a statistics marker. A statistics marker holds the stats for
onLocationChanged(loc); * the last segment up to this marker.
} *
}; * @param location the location where to insert
* @return the unique id of the inserted marker
*/
@Override
public long insertStatisticsMarker(Location location) {
return service.insertStatisticsMarker(location);
}
@Override
public void endCurrentTrack() {
service.endCurrentTrack();
}
@Override
public void deleteAllTracks() {
if (isRecording()) {
throw new IllegalStateException(
"Cannot delete all tracks while recording!");
}
service.providerUtils.deleteAllTracks();
}
@Override
public void recordLocation(Location loc) {
service.onLocationChanged(loc);
}
}
public long startNewTrack() { public long startNewTrack() {
Log.d(MyTracksConstants.TAG, "TrackRecordingService.startNewTrack"); Log.d(MyTracksConstants.TAG, "TrackRecordingService.startNewTrack");