forked from upstream-mirrors/OpenTracks
Making TrackDataHub an application singleton (using the application context to ensure we don't leak MyTracks).
This commit is contained in:
@@ -128,7 +128,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
|||||||
|
|
||||||
providerUtils = MyTracksProviderUtils.Factory.get(this);
|
providerUtils = MyTracksProviderUtils.Factory.get(this);
|
||||||
preferences = getSharedPreferences(Constants.SETTINGS_NAME, 0);
|
preferences = getSharedPreferences(Constants.SETTINGS_NAME, 0);
|
||||||
dataHub = new TrackDataHub(this, preferences, providerUtils);
|
dataHub = TrackDataHub.getInstance(this);
|
||||||
menuManager = new MenuManager(this);
|
menuManager = new MenuManager(this);
|
||||||
serviceBinder = TrackRecordingServiceBinder.getInstance(this);
|
serviceBinder = TrackRecordingServiceBinder.getInstance(this);
|
||||||
|
|
||||||
@@ -174,15 +174,6 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
|||||||
Eula.showEulaRequireAcceptance(this);
|
Eula.showEulaRequireAcceptance(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
Log.d(TAG, "MyTracks.onDestroy");
|
|
||||||
|
|
||||||
dataHub.destroy();
|
|
||||||
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
Log.d(TAG, "MyTracks.onStop");
|
Log.d(TAG, "MyTracks.onStop");
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import static com.google.android.apps.mytracks.Constants.MAX_NETWORK_AGE_MS;
|
|||||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||||
import static com.google.android.apps.mytracks.Constants.TARGET_DISPLAYED_TRACK_POINTS;
|
import static com.google.android.apps.mytracks.Constants.TARGET_DISPLAYED_TRACK_POINTS;
|
||||||
|
|
||||||
|
import com.google.android.apps.mytracks.Constants;
|
||||||
import com.google.android.apps.mytracks.content.DataSourceManager.DataSourceListener;
|
import com.google.android.apps.mytracks.content.DataSourceManager.DataSourceListener;
|
||||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.DoubleBufferedLocationFactory;
|
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.DoubleBufferedLocationFactory;
|
||||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationIterator;
|
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationIterator;
|
||||||
@@ -150,8 +151,8 @@ public class TrackDataHub {
|
|||||||
|
|
||||||
// Get content notifications on the main thread, send listener callbacks in another.
|
// Get content notifications on the main thread, send listener callbacks in another.
|
||||||
// This ensures listener calls are serialized.
|
// This ensures listener calls are serialized.
|
||||||
private final HandlerThread listenerHandlerThread;
|
private HandlerThread listenerHandlerThread;
|
||||||
private final Handler listenerHandler;
|
private Handler listenerHandler;
|
||||||
|
|
||||||
/** Manager for external listeners (those from activities). */
|
/** Manager for external listeners (those from activities). */
|
||||||
private final TrackDataListeners listeners;
|
private final TrackDataListeners listeners;
|
||||||
@@ -192,13 +193,24 @@ public class TrackDataHub {
|
|||||||
private int lastSamplingFrequency;
|
private int lastSamplingFrequency;
|
||||||
private DoubleBufferedLocationFactory locationFactory;
|
private DoubleBufferedLocationFactory locationFactory;
|
||||||
|
|
||||||
/**
|
private static TrackDataHub instance;
|
||||||
* Default constructor.
|
|
||||||
*/
|
public synchronized static TrackDataHub getInstance(Context context) {
|
||||||
public TrackDataHub(Context ctx, SharedPreferences preferences,
|
if (instance != null) {
|
||||||
MyTracksProviderUtils providerUtils) {
|
return instance;
|
||||||
this(ctx, new DataSourcesWrapperImpl(ctx, preferences), new TrackDataListeners(),
|
}
|
||||||
preferences, providerUtils, TARGET_DISPLAYED_TRACK_POINTS);
|
|
||||||
|
// Ensure our singleton is never bound to an activity, to avoid memory leaks.
|
||||||
|
context = context.getApplicationContext();
|
||||||
|
|
||||||
|
SharedPreferences preferences = context.getSharedPreferences(Constants.SETTINGS_NAME, 0);
|
||||||
|
MyTracksProviderUtils providerUtils = MyTracksProviderUtils.Factory.get(context);
|
||||||
|
instance = new TrackDataHub(context,
|
||||||
|
new DataSourcesWrapperImpl(context, preferences),
|
||||||
|
new TrackDataListeners(),
|
||||||
|
preferences, providerUtils,
|
||||||
|
TARGET_DISPLAYED_TRACK_POINTS);
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -222,10 +234,6 @@ public class TrackDataHub {
|
|||||||
METRIC_UNITS_KEY = context.getString(R.string.metric_units_key);
|
METRIC_UNITS_KEY = context.getString(R.string.metric_units_key);
|
||||||
SPEED_REPORTING_KEY = context.getString(R.string.report_speed_key);
|
SPEED_REPORTING_KEY = context.getString(R.string.report_speed_key);
|
||||||
|
|
||||||
listenerHandlerThread = new HandlerThread("trackDataContentThread");
|
|
||||||
listenerHandlerThread.start();
|
|
||||||
listenerHandler = new Handler(listenerHandlerThread.getLooper());
|
|
||||||
|
|
||||||
resetState();
|
resetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,6 +249,10 @@ public class TrackDataHub {
|
|||||||
}
|
}
|
||||||
started = true;
|
started = true;
|
||||||
|
|
||||||
|
listenerHandlerThread = new HandlerThread("trackDataContentThread");
|
||||||
|
listenerHandlerThread.start();
|
||||||
|
listenerHandler = new Handler(listenerHandlerThread.getLooper());
|
||||||
|
|
||||||
// This may or may not register internal listeners, depending on whether
|
// This may or may not register internal listeners, depending on whether
|
||||||
// we already had external listeners.
|
// we already had external listeners.
|
||||||
dataSourceManager.updateAllListeners(getNeededListenerTypes());
|
dataSourceManager.updateAllListeners(getNeededListenerTypes());
|
||||||
@@ -250,14 +262,6 @@ public class TrackDataHub {
|
|||||||
loadDataForAllListeners();
|
loadDataForAllListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadSharedPreferences() {
|
|
||||||
selectedTrackId = preferences.getLong(SELECTED_TRACK_KEY, -1);
|
|
||||||
useMetricUnits = preferences.getBoolean(METRIC_UNITS_KEY, true);
|
|
||||||
reportSpeed = preferences.getBoolean(SPEED_REPORTING_KEY, true);
|
|
||||||
minRequiredAccuracy = preferences.getInt(MIN_REQUIRED_ACCURACY_KEY,
|
|
||||||
DEFAULT_MIN_REQUIRED_ACCURACY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops listening to data sources and reporting the data to external
|
* Stops listening to data sources and reporting the data to external
|
||||||
* listeners.
|
* listeners.
|
||||||
@@ -273,16 +277,27 @@ public class TrackDataHub {
|
|||||||
dataSourceManager.unregisterAllListeners();
|
dataSourceManager.unregisterAllListeners();
|
||||||
|
|
||||||
started = false;
|
started = false;
|
||||||
|
|
||||||
|
listenerHandlerThread.getLooper().quit();
|
||||||
|
listenerHandlerThread = null;
|
||||||
|
listenerHandler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Permanently invalidates and throws away all resources used by this class. */
|
@Override
|
||||||
public void destroy() {
|
protected void finalize() throws Throwable {
|
||||||
if (started) {
|
if (started || listenerHandlerThread.isAlive()) {
|
||||||
throw new IllegalStateException("Can only destroy the data hub after it's been stopped");
|
Log.e(TAG, "Forgot to stop() TrackDataHub");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
super.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
ApiFeatures.getInstance().getApiPlatformAdapter()
|
private void loadSharedPreferences() {
|
||||||
.stopHandlerThread(listenerHandlerThread);
|
selectedTrackId = preferences.getLong(SELECTED_TRACK_KEY, -1);
|
||||||
|
useMetricUnits = preferences.getBoolean(METRIC_UNITS_KEY, true);
|
||||||
|
reportSpeed = preferences.getBoolean(SPEED_REPORTING_KEY, true);
|
||||||
|
minRequiredAccuracy = preferences.getInt(MIN_REQUIRED_ACCURACY_KEY,
|
||||||
|
DEFAULT_MIN_REQUIRED_ACCURACY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Updates known magnetic declination if needed. */
|
/** Updates known magnetic declination if needed. */
|
||||||
@@ -325,6 +340,10 @@ public class TrackDataHub {
|
|||||||
* is not available or doesn't have a fix.
|
* is not available or doesn't have a fix.
|
||||||
*/
|
*/
|
||||||
public void forceUpdateLocation() {
|
public void forceUpdateLocation() {
|
||||||
|
if (!started) {
|
||||||
|
Log.w(TAG, "Not started, not forcing location update");
|
||||||
|
return;
|
||||||
|
}
|
||||||
Log.i(TAG, "Forcing location update");
|
Log.i(TAG, "Forcing location update");
|
||||||
|
|
||||||
Location loc = dataSources.getLastKnownLocation();
|
Location loc = dataSources.getLastKnownLocation();
|
||||||
@@ -1059,6 +1078,12 @@ public class TrackDataHub {
|
|||||||
|
|
||||||
// @VisibleForTesting
|
// @VisibleForTesting
|
||||||
protected void runInListenerThread(Runnable runnable) {
|
protected void runInListenerThread(Runnable runnable) {
|
||||||
|
if (listenerHandler == null) {
|
||||||
|
// Use a Throwable to ensure the stack trace is logged.
|
||||||
|
Log.e(TAG, "Tried to use listener thread before start()", new Throwable());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
listenerHandler.post(runnable);
|
listenerHandler.post(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ public class TrackDataHubTest extends AndroidTestCase {
|
|||||||
AndroidMock.replay(dataSources);
|
AndroidMock.replay(dataSources);
|
||||||
|
|
||||||
hub.stop();
|
hub.stop();
|
||||||
hub.destroy();
|
hub = null;
|
||||||
|
|
||||||
super.tearDown();
|
super.tearDown();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user