2

Most of the common suggestions is, as it seems leaks are coming from context, use application context and detach location call from activity lifecycle. I did both previous thing and also later. But leak still existing. Here is my implementation with viewmodel,

public class LocationViewModel extends ViewModel {    
    private final MutableLiveData<Location> _locationLive = new MutableLiveData<>();
    public LiveData<Location> locationLive = _locationLive;
    private final static String TAG = "LocationViewModel";
    private final FusedLocationProviderClient mFusedLocationClient;
    private LocationRequest locationRequest;
    private WeakReference<LocationCallback> locationCallback;

    private static final long UPDATE_INTERVAL = 5000, FASTEST_INTERVAL = 3000;

    public LocationViewModel() {
        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(MyApp.getApplication());
        prepareGpsAccess();
    }

    private void prepareGpsAccess() {
        locationRequest = LocationRequest.create();
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        locationRequest.setInterval(UPDATE_INTERVAL); // 10 seconds
        locationRequest.setFastestInterval(FASTEST_INTERVAL); // 5 seconds

        locationCallback = new WeakReference<>(new LocationCallback() {
            @Override
            public void onLocationResult(@NonNull LocationResult locationResult) {
                for (Location location : locationResult.getLocations()) {
                    _locationLive.postValue(location);
                    stopLocationUpdates();
                }
            }
        });
    }

    public void stopLocationUpdates() {
        if (mFusedLocationClient != null && locationCallback.get() != null) {
            try {
                final Task<Void> voidTask = mFusedLocationClient.removeLocationUpdates(locationCallback.get());
                voidTask.addOnCompleteListener(task -> {
                    Log.e(TAG, "StopLocation addOnCompleteListener: " + task.isComplete());
                    if (task.isSuccessful()) {
                        Log.d(TAG, "StopLocation updates successful! ");
                    } else {
                        Log.d(TAG, "StopLocation updates unsuccessful! " + voidTask.toString());
                    }
                });

                voidTask.addOnSuccessListener(aVoid -> Log.e(TAG, "StopLocation addOnSuccessListener: "));
                voidTask.addOnFailureListener(e -> Log.e(TAG, "StopLocation addOnFailureListener: " + e.toString()));
            } catch (SecurityException exp) {
                Log.d(TAG, "StopLocation Security exception while removeLocationUpdates");
            }
        }
    }

    public void getLocation() {
        if (ActivityCompat.checkSelfPermission(MyApp.getApplication(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(MyApp.getApplication(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        mFusedLocationClient.requestLocationUpdates(locationRequest, locationCallback.get(), Looper.myLooper());
    }
}

Here is the LeakCanary output,

1 APPLICATION LEAKS
    
    References underlined with "~~~" are likely causes.
    Learn more at https://squ.re/leaks.
    
    549 bytes retained by leaking objects
    Signature: 567aad2e1a38902d91202892cd70b65cb7df4f3
    ┬───
    │ GC Root: Global variable in native code
    │
    ├─ com.google.android.gms.location.zzam instance
    │    Leaking: UNKNOWN
    │    Retaining 1.5 kB in 31 objects
    │    ↓ zzam.zza
    │           ~~~
    ├─ com.google.android.gms.location.zzx instance
    │    Leaking: UNKNOWN
    │    Retaining 807 B in 23 objects
    │    ↓ zzx.zzc
    │          ~~~
    ├─ com.package.LocationViewModel$1 instance
    │    Leaking: UNKNOWN
    │    Retaining 561 B in 17 objects
    │    Anonymous subclass of com.google.android.gms.location.LocationCallback
    │    ↓ LocationViewModel$1.this$0
    │                          ~~~~~~
    ╰→ com.package.LocationViewModel instance
    ​     Leaking: YES (ObjectWatcher was watching this because com.package.location.LocationViewModel received
    ​     ViewModel#onCleared() callback)
    ​     Retaining 549 B in 16 objects
    ​     key = f23437cd-aafa-4153-866b-2b8961646172
    ​     watchDurationMillis = 14454
    ​     retainedDurationMillis = 9452

I have tried numerous thing, still this leak exist. Please share if you know another way to avoid this leak.

There is a relevant issue still open here, https://github.com/android/location-samples/issues/100

Sujal Kumar
  • 1,054
  • 10
  • 23
asish
  • 4,767
  • 4
  • 29
  • 49

2 Answers2

0

So basically, this issue was fixed by updating google, before I was using 16.xx. Finally I updated to 17.0.0 and this issue were gone.

asish
  • 4,767
  • 4
  • 29
  • 49
0

Google has recently resolved this memory leak issue in their latest play-services-location:20.0.0 release. Release Note.

To know more about the history of this memory leak, check this and this.

Jatin
  • 491
  • 2
  • 12