Skip to content

Commit ad7898b

Browse files
Fix Memory Leaks (#368)
* upgrade to lost 2.3.0 snapshot * dont hold static reference to context in locationfactory * use application context in mapzenmanager * use weak reference in connection callbacks * use mock context that has mock application context in tests * cleanup listener in basic example * checkstyle
1 parent 17b25a2 commit ad7898b

File tree

10 files changed

+51
-37
lines changed

10 files changed

+51
-37
lines changed

core/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ dependencies {
7878

7979
compile "com.mapzen.tangram:tangram:$tangram_version"
8080
compile 'com.mapzen.android:pelias-android-sdk:1.1.0'
81-
compile 'com.mapzen.android:lost:2.2.0'
81+
compile 'com.mapzen.android:lost:2.3.0-SNAPSHOT'
8282

8383
compile 'com.google.dagger:dagger:2.0'
8484
compile 'javax.annotation:javax.annotation-api:1.2'

core/src/main/java/com/mapzen/android/core/MapzenManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public class MapzenManager {
3636
*/
3737
public static MapzenManager instance(Context context) {
3838
if (instance == null) {
39-
instance = new MapzenManager(context);
39+
instance = new MapzenManager(context.getApplicationContext());
4040
}
4141

4242
return instance;

core/src/main/java/com/mapzen/android/graphics/OverlayManager.java

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import android.view.View;
2020
import android.widget.ImageButton;
2121

22+
import java.lang.ref.WeakReference;
2223
import java.util.ArrayList;
2324
import java.util.HashMap;
2425
import java.util.List;
@@ -86,12 +87,6 @@ public class OverlayManager implements TouchInput.PanResponder, TouchInput.Rotat
8687
@Override public void onLocationChanged(Location location) {
8788
handleLocationChange(location);
8889
}
89-
90-
@Override public void onProviderEnabled(String provider) {
91-
}
92-
93-
@Override public void onProviderDisabled(String provider) {
94-
}
9590
};
9691

9792
View.OnClickListener compassExternalClickListener;
@@ -116,19 +111,19 @@ public class OverlayManager implements TouchInput.PanResponder, TouchInput.Rotat
116111

117112
private static class OverlayManagerConnectionCallbacks
118113
implements LostApiClient.ConnectionCallbacks {
119-
private OverlayManager overlayManager;
114+
private WeakReference<OverlayManager> overlayManager;
120115

121116
@Override public void onConnected() {
122-
if (overlayManager != null) {
123-
overlayManager.enableLocationLayer();
117+
if (overlayManager != null && overlayManager.get() != null) {
118+
overlayManager.get().enableLocationLayer();
124119
}
125120
}
126121

127122
@Override public void onConnectionSuspended() {
128123
}
129124

130125
public void setOverlayManager(OverlayManager overlayManager) {
131-
this.overlayManager = overlayManager;
126+
this.overlayManager = new WeakReference(overlayManager);
132127
}
133128
};
134129

core/src/main/java/com/mapzen/android/location/LocationFactory.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
public class LocationFactory {
1212

1313
private static LostApiClient shared;
14-
private static Context context;
1514

1615
/**
1716
* Returns shared {@link LostApiClient}.
@@ -23,9 +22,8 @@ public class LocationFactory {
2322
* and future attempts to bind the fused location service would fail.
2423
*/
2524
@Deprecated public static LostApiClient sharedClient(Context context) {
26-
if (LocationFactory.context != context) {
25+
if (shared == null) {
2726
shared = new LostApiClient.Builder(context).build();
28-
LocationFactory.context = context;
2927
}
3028

3129
return shared;
@@ -42,9 +40,8 @@ public class LocationFactory {
4240
*/
4341
@Deprecated public static LostApiClient sharedClient(Context context,
4442
ConnectionCallbacks callbacks) {
45-
if (LocationFactory.context != context) {
43+
if (shared == null) {
4644
shared = new LostApiClient.Builder(context).addConnectionCallbacks(callbacks).build();
47-
LocationFactory.context = context;
4845
}
4946

5047
return shared;

core/src/test/java/com/mapzen/android/core/MapzenManagerTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
import android.content.res.Resources;
1414
import android.support.annotation.NonNull;
1515

16+
import static com.mapzen.TestHelper.getMockContext;
1617
import static com.mapzen.android.core.MapzenManager.API_KEY_RES_NAME;
1718
import static com.mapzen.android.core.MapzenManager.API_KEY_RES_TYPE;
1819
import static org.assertj.core.api.Assertions.assertThat;
19-
import static org.mockito.Mockito.mock;
2020
import static org.mockito.Mockito.when;
2121

2222
@RunWith(PowerMockRunner.class)
@@ -27,7 +27,7 @@ public class MapzenManagerTest {
2727
}
2828

2929
@Test public void shouldNotBeNull() throws Exception {
30-
Context context = mock(Context.class);
30+
Context context = getMockContext();
3131
Resources resources = new TestResources();
3232
when(context.getResources()).thenReturn(resources);
3333
MapzenManager mapzenManager = MapzenManager.instance(context);
@@ -36,7 +36,7 @@ public class MapzenManagerTest {
3636

3737
@Test(expected = IllegalStateException.class)
3838
public void getApiKey_shouldReturnThrowIfNotSet() throws Exception {
39-
Context context = mock(Context.class);
39+
Context context = getMockContext();
4040
Resources resources = new TestResources();
4141
when(context.getResources()).thenReturn(resources);
4242
MapzenManager mapzenManager = MapzenManager.instance(context);
@@ -45,15 +45,15 @@ public void getApiKey_shouldReturnThrowIfNotSet() throws Exception {
4545

4646
@Test(expected = IllegalStateException.class)
4747
public void getApiKey_shouldReturnThrowIfResourceNotFound() throws Exception {
48-
Context context = mock(Context.class);
48+
Context context = getMockContext();
4949
Resources resources = new TestResourcesNotFound();
5050
when(context.getResources()).thenReturn(resources);
5151
MapzenManager mapzenManager = MapzenManager.instance(context);
5252
mapzenManager.getApiKey();
5353
}
5454

5555
@Test public void getApiKey_shouldReturnStringResourceValue() throws Exception {
56-
Context context = mock(Context.class);
56+
Context context = getMockContext();
5757
TestResources resources = new TestResources();
5858
resources.testApiKey = "mapzen-fake-api-key";
5959
when(context.getResources()).thenReturn(resources);
@@ -62,7 +62,7 @@ public void getApiKey_shouldReturnThrowIfResourceNotFound() throws Exception {
6262
}
6363

6464
@Test public void setApiKey_shouldOverrideStringResourcesValue() throws Exception {
65-
Context context = mock(Context.class);
65+
Context context = getMockContext();
6666
TestResources resources = new TestResources();
6767
resources.testApiKey = "mapzen-fake-api-key";
6868
when(context.getResources()).thenReturn(resources);

core/src/test/java/com/mapzen/android/graphics/MapInitializerTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ public class MapInitializerTest {
4949

5050
@Test public void init_shouldReturnMapzenMap() throws Exception {
5151
final TestCallback callback = new TestCallback();
52-
final TestMapView mapView = new TestMapView();
52+
final TestMapView mapView = mock(TestMapView.class);
53+
Context context = mock(Context.class);
54+
when(context.getApplicationContext()).thenReturn(mock(Context.class));
55+
when(mapView.getContext()).thenReturn(context);
56+
when(mapView.getTangramMapView()).thenCallRealMethod();
5357
MapzenManager.instance(getMockContext()).setApiKey("fake-mapzen-api-key");
5458
mapInitializer.init(mapView, callback);
5559
assertThat(callback.map).isInstanceOf(MapzenMap.class);

core/src/test/java/com/mapzen/android/location/LocationFactoryTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,16 @@ public class LocationFactoryTest {
3838
.isEqualTo(LocationFactory.sharedClient(context, callbacks));
3939
}
4040

41-
@Test public void sharedClient_shouldReturnNewClientForNewContext() throws Exception {
41+
@Test public void sharedClient_shouldReturnSameClientForNewContext() throws Exception {
4242
assertThat(LocationFactory.sharedClient(Mockito.mock(Context.class)))
43-
.isNotEqualTo(LocationFactory.sharedClient(Mockito.mock(Context.class)));
43+
.isEqualTo(LocationFactory.sharedClient(Mockito.mock(Context.class)));
4444
}
4545

46-
@Test public void sharedClientWithCallbacks_shouldReturnNewClientForNewContext()
46+
@Test public void sharedClientWithCallbacks_shouldReturnSameClientForNewContext()
4747
throws Exception {
4848
LostApiClient.ConnectionCallbacks callbacks = new TestConnectionCallbacks();
4949
assertThat(LocationFactory.sharedClient(Mockito.mock(Context.class), callbacks))
50-
.isNotEqualTo(LocationFactory.sharedClient(Mockito.mock(Context.class), callbacks));
50+
.isEqualTo(LocationFactory.sharedClient(Mockito.mock(Context.class), callbacks));
5151
}
5252

5353
private class TestConnectionCallbacks implements LostApiClient.ConnectionCallbacks {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.mapzen.android.lost.internal;
2+
3+
/**
4+
* Created by sarahlensing on 5/11/17.
5+
*/
6+
7+
public class FusedApiServiceUpdater {
8+
9+
public static void updateApiService(FusedLocationProviderApiImpl api,
10+
IFusedLocationProviderService service) {
11+
api.service = service;
12+
}
13+
}

mapzen-places-api/src/test/java/com/mapzen/places/api/internal/PlaceAutocompletePresenterTest.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
import com.mapzen.android.lost.api.LocationServices;
44
import com.mapzen.android.lost.api.LostApiClient;
5+
import com.mapzen.android.lost.internal.FusedApiServiceUpdater;
56
import com.mapzen.android.lost.internal.FusedLocationProviderApiImpl;
6-
import com.mapzen.android.lost.internal.FusedLocationProviderService;
7+
import com.mapzen.android.lost.internal.IFusedLocationProviderService;
78
import com.mapzen.pelias.BoundingBox;
89
import com.mapzen.pelias.gson.Feature;
910
import com.mapzen.pelias.gson.Properties;
@@ -56,21 +57,20 @@ public void getBoundingBox_shouldReturnCorrectBox() throws Exception {
5657

5758
@Test
5859
public void getBoundingBox_lostClient_shouldReturnCorrectBox() throws Exception {
59-
FusedLocationProviderService.FusedLocationProviderBinder stubBinder =
60-
mock(FusedLocationProviderService.FusedLocationProviderBinder.class);
61-
FusedLocationProviderService mockService = mock(FusedLocationProviderService.class);
62-
when(stubBinder.getService()).thenReturn(mockService);
6360
FusedLocationProviderApiImpl impl =
6461
(FusedLocationProviderApiImpl) LocationServices.FusedLocationApi;
65-
impl.onServiceConnected(stubBinder);
62+
IFusedLocationProviderService service = mock(IFusedLocationProviderService.class);
63+
FusedApiServiceUpdater.updateApiService(impl, service);
6664

6765
LostApiClient client = mock(LostApiClient.class);
66+
when(client.isConnected()).thenReturn(true);
67+
presenter.setLostClient(client);
68+
6869
Location location = mock(Location.class);
6970
when(location.getLatitude()).thenReturn(40.0);
7071
when(location.getLongitude()).thenReturn(70.0);
71-
when(mockService.getLastLocation(client)).thenReturn(location);
72-
presenter.setLostClient(client);
73-
when(client.isConnected()).thenReturn(true);
72+
when(service.getLastLocation()).thenReturn(location);
73+
7474
BoundingBox boundingBox = presenter.getBoundingBox();
7575
double boundsRadius = 0.02;
7676
assertThat(boundingBox.getMinLat() + boundsRadius).isEqualTo(location.getLatitude());

samples/mapzen-android-sdk-sample/src/main/java/com/mapzen/android/sdk/sample/BasicMapzenActivity.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ private void configureMap(boolean enabled) {
8787
}
8888
}
8989

90+
@Override protected void onDestroy() {
91+
super.onDestroy();
92+
map.setFindMeOnClickListener(null);
93+
}
94+
9095
@Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
9196
if (map == null) {
9297
return;

0 commit comments

Comments
 (0)